This commit is contained in:
atomiks
2018-02-26 00:14:39 +11:00
commit 767b720f10
46 changed files with 15488 additions and 0 deletions

3
.babelrc Normal file
View File

@ -0,0 +1,3 @@
{
"presets": ["env", "stage-2"]
}

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules/
.cache/
.DS_Store

44
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,44 @@
### Snippet name
Brief description
#### HTML
```html
```
#### CSS
```css
```
#### Demo
<!-- You must create a `snippet-demo` parent block and use it as a namespace with BEM syntax. -->
<div class="snippet-demo">
<some-element class="snippet-demo__snippet-name"></some-element>
</div>
<!-- Add your style rules here. -->
<style>
</style>
#### Explanation
<!-- Use a step-by-step (ordered) list if possible. Keep it concise. -->
#### Browser support
<!-- Use the checkmark or the warning emoji, see the existing snippets. -->
<span class="snippet__support-note">✅ No caveats.</span>
<!-- Whenever possible, link a `caniuse` feature which allows the browser support percentage to be displayed.
If no link is provided, it defaults to 99+%. -->
* https://caniuse.com/#feat=some-feature

12
README.md Normal file
View File

@ -0,0 +1,12 @@
# 30 Seconds of CSS
A curated collection of useful CSS snippets you can understand in 30 seconds or less.
Inspired by [30 seconds of code](https://github.com/Chalarangelo/30-seconds-of-code).
## View online
https://github.io/atomiks/30-seconds-of-css
## Contributing
See CONTRIBUTING.md for the snippet template.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

218
dist/index.html vendored Normal file

File diff suppressed because one or more lines are too long

BIN
favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

987
index.html Normal file
View File

@ -0,0 +1,987 @@
<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">
<script src="./src/js/index.js" defer=""></script>
</head>
<body>
<button class="back-to-top-button"></button>
<aside class="sidebar">
<button class="hamburger hamburger--spin sidebar__menu" type="button">
<span class="hamburger-box">
<span class="hamburger-inner"></span>
</span>
</button>
<div class="sidebar__links">
<a class="sidebar__link" href="#clearfix">Clearfix</a>
<a class="sidebar__link" href="#custom-text-selection">Custom text selection</a>
<a class="sidebar__link" href="#easing-variables">Easing variables</a>
<a class="sidebar__link" href="#etched-text">Etched text</a>
<a class="sidebar__link" href="#gradient-text">Gradient text</a>
<a class="sidebar__link" href="#hairline-border">Hairline border</a>
<a class="sidebar__link" href="#horizontal-and-vertical-centering">Horizontal and vertical centering</a>
<a class="sidebar__link" href="#mouse-cursor-gradient-tracking">Mouse cursor gradient tracking</a>
<a class="sidebar__link" href="#overflow-scroll-gradient">Overflow scroll gradient</a>
<a class="sidebar__link" href="#popout-menu">Popout menu</a>
<a class="sidebar__link" href="#pretty-text-underline">Pretty text underline</a>
<a class="sidebar__link" href="#shape-separator">Shape separator</a>
<a class="sidebar__link" href="#system-font-stack">System font stack</a>
<a class="sidebar__link" href="#triangle">Triangle</a>
<a class="sidebar__link" href="#truncate-text">Truncate text</a>
</div>
</aside>
<div class="content-wrapper">
<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>
</div>
</header>
<main class="main">
<div class="container">
<div class="snippet">
<h3 id="clearfix">Clearfix</h3>
<p>Ensures that an element self-clears its children.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;div class="clearfix"&gt;
&lt;div class="floated"&gt;float a&lt;/div&gt;
&lt;div class="floated"&gt;float b&lt;/div&gt;
&lt;div class="floated"&gt;float c&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">.clearfix::after {
content: '';
display: table;
clear: both;
}
.floated {
float: left;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<div class="snippet-demo">
<div class="snippet-demo__clearfix">
<div class="snippet-demo__floated">float a</div>
<div class="snippet-demo__floated">float b</div>
<div class="snippet-demo__floated">float c</div>
</div>
</div>
<style>
.snippet-demo__clearfix::after {
content: '';
display: table;
clear: both;
}
.snippet-demo__floated {
float: left;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<ol>
<li><code>.clearfix::after</code> defines a pseudo element.</li>
<li><code>content: ''</code> allows the pseudo element to affect layout.</li>
<li><code>clear: both</code> indicates that the left, right or both sides of the element cannot be adjacent to earlier floated elements within the same block formatting context.</li>
</ol>
<h4 data-type="Browser support">Browser support</h4>
<div>
<div class="snippet__browser-support">
99+%
</div>
</div>
<p><span class="snippet__support-note">✅ No caveats.</span></p>
</div>
<div class="snippet">
<h3 id="custom-text-selection">Custom text selection</h3>
<p>Changes the styling of text selection.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;p class="custom-text-selection"&gt;Select some of this text.&lt;/p&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">.text-selection::selection {
background: red;
color: white;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<div class="snippet-demo">
<p class="snippet-demo__custom-text-selection">Select some of this text.</p>
</div>
<style>
.snippet-demo__custom-text-selection::selection {
background: red;
color: white;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<p><code>::selection</code> defines a pseudo selector on an element to style text within it when selected.</p>
<h4 data-type="Browser support">Browser support</h4>
<div>
<div class="snippet__browser-support">
84.6%
</div>
</div>
<p><span class="snippet__support-note">⚠️ Requires prefixes for full support.</span></p>
<ul>
<li>
<a href="https://caniuse.com/#feat=css-selection" target="_blank">https://caniuse.com/#feat=css-selection</a>
</li>
</ul>
</div>
<div class="snippet">
<h3 id="easing-variables">Easing variables</h3>
<p>Variables that can be reused for <code>transition-timing-function</code> properties, more powerful than the built-in <code>ease</code>, <code>ease-in</code>, <code>ease-out</code> and <code>ease-in-out</code>.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;div class="easing-variables"&gt;&lt;/div&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">:root {
--ease-in-quad: cubic-bezier(0.55, 0.085, 0.68, 0.53);
--ease-in-cubic: cubic-bezier(0.55, 0.055, 0.675, 0.19);
--ease-in-quart: cubic-bezier(0.895, 0.03, 0.685, 0.22);
--ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06);
--ease-in-expo: cubic-bezier(0.95, 0.05, 0.795, 0.035);
--ease-in-circ: cubic-bezier(0.6, 0.04, 0.98, 0.335);
--ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94);
--ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1);
--ease-out-quart: cubic-bezier(0.165, 0.84, 0.44, 1);
--ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1);
--ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1);
--ease-out-circ: cubic-bezier(0.075, 0.82, 0.165, 1);
--ease-in-out-quad: cubic-bezier(0.455, 0.03, 0.515, 0.955);
--ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1);
--ease-in-out-quart: cubic-bezier(0.77, 0, 0.175, 1);
--ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1);
--ease-in-out-expo: cubic-bezier(1, 0, 0, 1);
--ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.easing-variables {
width: 50px;
height: 50px;
background: #333;
transition: transform 1s var(--ease-out-quart);
}
.easing-variables:hover {
transform: rotate(45deg);
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<div class="snippet-demo">
<div class="snippet-demo__easing-variables">Hover</div>
</div>
<style>
:root {
--ease-in-quad: cubic-bezier(0.55, 0.085, 0.68, 0.53);
--ease-in-cubic: cubic-bezier(0.55, 0.055, 0.675, 0.19);
--ease-in-quart: cubic-bezier(0.895, 0.03, 0.685, 0.22);
--ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06);
--ease-in-expo: cubic-bezier(0.95, 0.05, 0.795, 0.035);
--ease-in-circ: cubic-bezier(0.6, 0.04, 0.98, 0.335);
--ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94);
--ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1);
--ease-out-quart: cubic-bezier(0.165, 0.84, 0.44, 1);
--ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1);
--ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1);
--ease-out-circ: cubic-bezier(0.075, 0.82, 0.165, 1);
--ease-in-out-quad: cubic-bezier(0.455, 0.03, 0.515, 0.955);
--ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1);
--ease-in-out-quart: cubic-bezier(0.77, 0, 0.175, 1);
--ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1);
--ease-in-out-expo: cubic-bezier(1, 0, 0, 1);
--ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.snippet-demo__easing-variables {
width: 75px;
height: 75px;
background: #333;
color: white;
font-size: 0.8rem;
font-weight: bold;
display: flex;
justify-content: center;
align-items: center;
transition: transform 1s var(--ease-out-quart);
}
.snippet-demo__easing-variables:hover {
transform: rotate(45deg);
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<p>The variables are defined globally with the <code>:root</code> CSS pseudo-class, which matches the root element of a tree representing the document. In HTML, <code>:root</code> represents the <code>&lt;html&gt;</code> element and is identical
to the selector html, except that its specificity is higher.</p>
<h4 data-type="Browser support">Browser support</h4>
<div>
<div class="snippet__browser-support">
87.2%
</div>
</div>
<p><span class="snippet__support-note">✅ No caveats.</span></p>
<ul>
<li>
<a href="https://caniuse.com/#feat=css-variables" target="_blank">https://caniuse.com/#feat=css-variables</a>
</li>
</ul>
</div>
<div class="snippet">
<h3 id="etched-text">Etched text</h3>
<p>Creates an effect where text appears to be "etched" or engraved into the background.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;p class="etched-text"&gt;I appear etched into the background.&lt;/p&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">.etched-text {
text-shadow: 0 2px white;
font-size: 1.5rem;
font-weight: bold;
color: #b8bec5;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<div class="snippet-demo">
<p class="snippet-demo__etched-text">I appear etched into the background.</p>
</div>
<style>
.snippet-demo__etched-text {
font-size: 1.5rem;
font-weight: bold;
color: #b8bec5;
text-shadow: 0 2px 0 white;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<p><code>text-shadow: 0 2px white</code> creates a white shadow offset <code>0px</code> horizontally and <code>2px</code> vertically from the origin position.</p>
<p>The background must be darker than the shadow for the effect to work.</p>
<p>The text color should be slightly faded to make it look like it's engraved/carved out of the background.</p>
<h4 data-type="Browser support">Browser support</h4>
<div>
<div class="snippet__browser-support">
97.9%
</div>
</div>
<p><span class="snippet__support-note">✅ No caveats.</span></p>
<ul>
<li>
<a href="https://caniuse.com/#feat=css-textshadow" target="_blank">https://caniuse.com/#feat=css-textshadow</a>
</li>
</ul>
</div>
<div class="snippet">
<h3 id="gradient-text">Gradient text</h3>
<p>Gives text a gradient color.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;p class="gradient-text"&gt;Gradient text&lt;/p&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">.gradient-text {
background: -webkit-linear-gradient(pink, red);
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<div class="snippet-demo">
<p class="snippet-demo__gradient-text">
Gradient text
</p>
</div>
<style>
.snippet-demo__gradient-text {
background: -webkit-linear-gradient(pink, red);
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
font-size: 2rem;
font-weight: bold;
margin: 0;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<ol>
<li><code>background: -webkit-linear-gradient(...)</code> gives the text element a gradient background.</li>
<li><code>webkit-text-fill-color: transparent</code> fills the text with a transparent color.</li>
<li><code>webkit-background-clip: text</code> clips the background with the text, filling the text with the gradient background as the color.</li>
</ol>
<h4 data-type="Browser support">Browser support</h4>
<div>
<div class="snippet__browser-support">
90.7%
</div>
</div>
<p><span class="snippet__support-note">⚠️ Uses non-standard properties.</span></p>
<ul>
<li>
<a href="https://caniuse.com/#feat=text-stroke" target="_blank">https://caniuse.com/#feat=text-stroke</a>
</li>
</ul>
</div>
<div class="snippet">
<h3 id="hairline-border">Hairline border</h3>
<p>Gives an element a border equal to 1 native device pixel in width, which can look very sharp and crisp.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;div class="hairline-border"&gt;text&lt;/div&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">.hairline-border {
box-shadow: 0 0 0 1px;
}
@media (min-resolution: 2dppx) {
.hairline-border {
box-shadow: 0 0 0 0.5px;
}
}
@media (min-resolution: 3dppx) {
.hairline-border {
box-shadow: 0 0 0 0.33333333px;
}
}
@media (min-resolution: 4dppx) {
.hairline-border {
box-shadow: 0 0 0 0.25px;
}
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<div class="snippet-demo">
<p class="snippet-demo__hairline-border">Text with a hairline border around it.</p>
</div>
<style>
.snippet-demo__hairline-border {
box-shadow: 0 0 0 1px;
}
@media (min-resolution: 2dppx) {
.snippet-demo__hairline-border {
box-shadow: 0 0 0 0.5px;
}
}
@media (min-resolution: 3dppx) {
.snippet-demo__hairline-border {
box-shadow: 0 0 0 0.33333333px;
}
}
@media (min-resolution: 4dppx) {
.snippet-demo__hairline-border {
box-shadow: 0 0 0 0.25px;
}
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<ol>
<li><code>box-shadow</code>, when only using spread, adds a psuedo-border which can use subpixels*.</li>
<li>Use <code>@media (min-resolution: ...)</code> to check the device pixel ratio (<code>1ddpx</code> equals 96 DPI), setting the spread of the <code>box-shadow</code> equal to <code>1 / dppx</code>.</li>
</ol>
<h4 data-type="Browser Support">Browser Support</h4>
<div>
<div class="snippet__browser-support">
95.0%
</div>
</div>
<p><span class="snippet__support-note">⚠️ Needs alternate syntax and JavaScript user agent checking for full support.</span></p>
<ul>
<li>
<a href="https://caniuse.com/#feat=css-boxshadow" target="_blank">https://caniuse.com/#feat=css-boxshadow</a>
</li>
<li>
<a href="https://caniuse.com/#feat=css-media-resolution" target="_blank">https://caniuse.com/#feat=css-media-resolution</a>
</li>
</ul>
<hr>
<p>*Chrome does not support subpixel values on <code>border</code>. Safari does not support subpixel values on <code>box-shadow</code>. Firefox supports subpixel values on both.</p>
</div>
<div class="snippet">
<h3 id="horizontal-and-vertical-centering">Horizontal and vertical centering</h3>
<p>Horizontally and vertically centers a child element within a parent element.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;div class="horizontal-and-vertical-centering"&gt;
&lt;div class="child"&gt;&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">.horizontal-and-vertical-centering {
display: flex;
justify-content: center;
align-items: center;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<div class="snippet-demo">
<div class="snippet-demo__horizontal-and-vertical-centering">
<p class="snippet-demo__horizontal-and-vertical-centering__child">Centered content.</p>
</div>
</div>
<style>
.snippet-demo__horizontal-and-vertical-centering {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<ol>
<li><code>display: flex</code> enables flexbox.</li>
<li><code>justify-content: center</code> centers the child horizontally.</li>
<li><code>align-items: center</code> centers the child vertically.</li>
</ol>
<h4 data-type="Browser support">Browser support</h4>
<div>
<div class="snippet__browser-support">
97.8%
</div>
</div>
<p><span class="snippet__support-note">⚠️ Needs prefixes for full support.</span></p>
<ul>
<li>
<a href="https://caniuse.com/#feat=flexbox" target="_blank">https://caniuse.com/#feat=flexbox</a>
</li>
</ul>
</div>
<div class="snippet">
<h3 id="mouse-cursor-gradient-tracking">Mouse cursor gradient tracking</h3>
<p>A hover effect where the gradient follows the mouse cursor.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;button class="mouse-cursor-gradient-tracking"&gt;
&lt;span&gt;Hover me&lt;/span&gt;
&lt;/button&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">.mouse-cursor-gradient-tracking {
position: relative;
background: #2379f7;
padding: 0.5rem 1rem;
font-size: 1.2rem;
border: none;
color: white;
cursor: pointer;
outline: none;
overflow: hidden;
}
.mouse-cursor-gradient-tracking span {
position: relative;
}
.mouse-cursor-gradient-tracking::before {
--size: 0;
content: '';
position: absolute;
left: var(--x);
top: var(--y);
width: var(--size);
height: var(--size);
background: radial-gradient(circle closest-side, pink, transparent);
transform: translate(-50%, -50%);
transition: width .2s ease, height .2s ease;
}
.mouse-cursor-gradient-tracking:hover::before {
--size: 200px;
}
</code></pre>
<h4 data-type="JavaScript">JavaScript</h4><pre><code class="lang-js">var btn = document.querySelector('.mouse-cursor-gradient-tracking')
btn.onmousemove = function (e) {
var x = e.pageX - btn.offsetLeft
var y = e.pageY - btn.offsetTop
btn.style.setProperty('--x', x + 'px')
btn.style.setProperty('--y', y + 'px')
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<div class="snippet-demo">
<button class="snippet-demo__mouse-cursor-gradient-tracking">
<span>Hover me</span>
</button>
</div>
<style>
.snippet-demo__mouse-cursor-gradient-tracking {
position: relative;
background: #2379f7;
padding: 0.5rem 1rem;
font-size: 1.2rem;
border: none;
color: white;
cursor: pointer;
outline: none;
overflow: hidden;
}
.snippet-demo__mouse-cursor-gradient-tracking span {
position: relative;
}
.snippet-demo__mouse-cursor-gradient-tracking::before {
--size: 0;
content: '';
position: absolute;
left: var(--x);
top: var(--y);
width: var(--size);
height: var(--size);
background: radial-gradient(circle closest-side, aqua, rgba(0, 255, 255, 0.0001));
transform: translate(-50%, -50%);
transition: width .2s ease, height .2s ease;
}
.snippet-demo__mouse-cursor-gradient-tracking:hover::before {
--size: 200px;
}
</style>
<script>
(function() {
var btn = document.querySelector('.snippet-demo__mouse-cursor-gradient-tracking')
btn.onmousemove = function(e) {
var x = e.pageX - btn.offsetLeft - btn.offsetParent.offsetLeft
var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
btn.style.setProperty('--x', x + 'px')
btn.style.setProperty('--y', y + 'px')
}
})()
</script>
<h4 data-type="Explanation">Explanation</h4>
<p><em>TODO</em></p>
<p><strong>Note!</strong></p>
<p>If the element's parent has a positioning context (<code>position: relative</code>), you will need to subtract its offsets as well.</p>
<pre><code class="lang-js">var x = e.pageX - btn.offsetLeft - btn.offsetParent.offsetLeft
var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
</code></pre>
<h4 data-type="Browser support">Browser support</h4>
<div>
<div class="snippet__browser-support">
87.2%
</div>
</div>
<p></p>
<div class="snippet__requires-javascript">Requires JavaScript</div>
<span class="snippet__support-note">⚠️ Requires JavaScript.</span>
<p></p>
<ul>
<li>
<a href="https://caniuse.com/#feat=css-variables" target="_blank">https://caniuse.com/#feat=css-variables</a>
</li>
</ul>
</div>
<div class="snippet">
<h3 id="overflow-scroll-gradient">Overflow scroll gradient</h3>
<p>Adds a fading gradient to an overflowing element to better indicate there is more content to be scrolled.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;div class="overflow-scroll-gradient"&gt;
&lt;div class="overflow-scroll-gradient__scroller"&gt;
Content to be scrolled
&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">.overflow-scroll-gradient {
position: relative;
}
.overflow-scroll-gradient::after {
content: '';
position: absolute;
bottom: 0;
width: 300px;
height: 25px;
background: linear-gradient(rgba(255, 255, 255, 0.001), white); /* transparent keyword is broken in Safari */
}
.overflow-scroll-gradient__scroller {
overflow-y: scroll;
background: white;
width: 300px;
height: 250px;
line-height: 1.2;
text-align: center;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<div class="snippet-demo">
<div class="snippet-demo__overflow-scroll-gradient">
<div class="snippet-demo__overflow-scroll-gradient__scroller">
Content to be scrolled
</div>
</div>
</div>
<style>
.snippet-demo__overflow-scroll-gradient {
position: relative;
}
.snippet-demo__overflow-scroll-gradient::after {
content: '';
background: linear-gradient(rgba(255, 255, 255, 0.001), white);
position: absolute;
width: 300px;
height: 25px;
bottom: 0;
}
.snippet-demo__overflow-scroll-gradient__scroller {
overflow-y: scroll;
background: white;
width: 300px;
height: 250px;
line-height: 1.2;
text-align: center;
}
</style>
<script>
document.querySelector('.snippet-demo__overflow-scroll-gradient__scroller').innerHTML = 'content '.repeat(200)
</script>
<h4 data-type="Explanation">Explanation</h4>
<ol>
<li><code>position: relative</code> on the parent establishes a Cartesian positioning context for psuedo elements.</li>
<li><code>::after</code> defines a pseudo element.</li>
<li><code>background-image: linear-gradient(...)</code> adds a linear gradient that fades from transparent to white (top to bottom).</li>
<li><code>position: absolute</code> takes the pseudo element out of the flow of the document and positions it in relation to the parent.</li>
<li><code>width: 300px</code> matches the size of the scrolling element (which is a child of the parent that has the pseudo element).</li>
<li><code>height: 25px</code> is the height of the fading gradient psuedo element, which should be kept relatively small.</li>
<li><code>bottom: 0</code> positions the pseudo element at the bottom of the parent.</li>
</ol>
<h4 data-type="Browser support">Browser support</h4>
<div>
<div class="snippet__browser-support">
94.8%
</div>
</div>
<p><span class="snippet__support-note">✅ No caveats.</span></p>
<ul>
<li>
<a href="https://caniuse.com/#feat=css-gradients" target="_blank">https://caniuse.com/#feat=css-gradients</a>
</li>
</ul>
</div>
<div class="snippet">
<h3 id="popout-menu">Popout menu</h3>
<p>Reveals an interactive popout menu on hover.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;div class="reference"&gt;
&lt;div class="popout-menu"&gt;
Popout menu
&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">.reference {
position: relative;
}
.popout-menu {
position: absolute;
visibility: hidden;
left: 100%;
}
.reference:hover &gt; .popout-menu {
visibility: visible;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<div class="snippet-demo">
<div class="snippet-demo__reference">
<div class="snippet-demo__popout-menu">
Popout menu
</div>
</div>
</div>
<style>
.snippet-demo__reference {
background: linear-gradient(135deg, #ff4c9f, #ff7b74);
height: 75px;
width: 75px;
position: relative;
will-change: transform;
}
.snippet-demo__popout-menu {
position: absolute;
visibility: hidden;
left: 100%;
background: #333;
color: white;
font-size: 0.9rem;
padding: 0.4rem 0.8rem;
width: 100px;
text-align: center;
}
.snippet-demo__reference:hover>.snippet-demo__popout-menu {
visibility: visible;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<ol>
<li><code>position: relative</code> on the reference parent establishes a Cartesian positioning context for its child.</li>
<li><code>position: absolute</code> takes the popout menu out of the flow of the document and positions it in relation to the parent.</li>
<li><code>left: 100%</code> moves the the popout menu 100% of its parent's width from the left.</li>
<li><code>visibility: hidden</code> hides the popout menu initially and allows for transitions (unlike <code>display: none</code>).</li>
<li><code>.reference:hover &gt; .popout-menu</code> means that when <code>.reference</code> is hovered over, select immediate children with a class of <code>.popout-menu</code> and change their <code>visibility</code> to <code>visible</code>,
which shows the popout.</li>
</ol>
<h4 data-type="Browser support">Browser support</h4>
<div>
<div class="snippet__browser-support">
99+%
</div>
</div>
<p><span class="snippet__support-note">✅ No caveats.</span></p>
</div>
<div class="snippet">
<h3 id="pretty-text-underline">Pretty text underline</h3>
<p>A nicer alternative to <code>text-decoration: underline</code> where descenders do not clip the underline. Natively implemented as <code>text-decoration-skip-ink: auto</code> but it has less control over the underline.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;p class="pretty-text-underline"&gt;Pretty text underline without clipping descending letters.&lt;/p&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">.pretty-text-underline {
display: inline;
font-size: 1.25rem;
text-shadow: 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-position: 0 1.04em;
background-repeat: repeat-x;
background-size: 1px 1px;
}
.pretty-text-underline::selection {
background-color: rgba(0, 150, 255, 0.3);
text-shadow: none;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<div class="snippet-demo">
<p class="snippet-demo__pretty-text-underline">Pretty text underline without clipping descending letters.</p>
</div>
<style>
.snippet-demo__pretty-text-underline {
display: inline;
font-size: 18px !important;
text-shadow: 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-position: 0 1.04em;
background-repeat: repeat-x;
background-size: 1px 1px;
}
.snippet-demo__pretty-text-underline::selection {
background-color: rgba(0, 150, 255, 0.3);
text-shadow: none;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<ol>
<li><code>text-shadow: ...</code> has 4 values with offsets that cover a 4x4 px area to ensure the underline has a "thick" shadow that covers the line where descenders clip it. Use a color that matches the background. For a larger font, use
a larger <code>px</code> size.</li>
<li><code>background-image: linear-gradient(...)</code> creates a 90deg gradient with the current text color (<code>currentColor</code>).</li>
<li>The <code>background-*</code> properties size the gradient as 1x1px at the bottom and repeats it along the x-axis.</li>
<li>The <code>::selection</code> pseudo selector ensures the text shadow does not interfere with text selection.
</li>
</ol>
<h4 data-type="Browser support">Browser support</h4>
<div>
<div class="snippet__browser-support">
94.8%
</div>
</div>
<p><span class="snippet__support-note">✅ No caveats.</span></p>
<ul>
<li>
<a href="https://caniuse.com/#feat=css-textshadow" target="_blank">https://caniuse.com/#feat=css-textshadow</a>
</li>
<li>
<a href="https://caniuse.com/#feat=css-gradients" target="_blank">https://caniuse.com/#feat=css-gradients</a>
</li>
</ul>
</div>
<div class="snippet">
<h3 id="shape-separator">Shape separator</h3>
<p>Uses an SVG shape to separate two different blocks to create more a interesting visual appearance compared to standard horizontal separation.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;div class="shape-separator"&gt;&lt;/div&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">.shape-separator {
position: relative;
height: 48px;
}
.shape-separator::after {
content: '';
background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxLjQxNCI+PHBhdGggZD0iTTEyIDEybDEyIDEySDBsMTItMTJ6IiBmaWxsPSIjZmZmIi8+PC9zdmc+);
position: absolute;
width: 100%;
height: 24px;
bottom: 0;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<div class="snippet-demo is-distinct">
<div class="snippet-demo__shape-separator"></div>
</div>
<style>
.snippet-demo__shape-separator {
position: relative;
height: 48px;
margin: -0.75rem -1.25rem;
}
.snippet-demo__shape-separator::after {
content: '';
background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxLjQxNCI+PHBhdGggZD0iTTEyIDEybDEyIDEySDBsMTItMTJ6IiBmaWxsPSIjZmZmIi8+PC9zdmc+);
position: absolute;
width: 100%;
height: 24px;
bottom: 0;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<ol>
<li><code>position: relative</code> on the element establishes a Cartesian positioning context for psuedo elements.</li>
<li><code>::after</code> defines a pseudo element.</li>
<li><code>background-image: url(...)</code> adds the SVG shape (a 24x24 triangle in base64 format) as the background image of the psuedo element, which repeats by default. It must be the same color as the block that is being separated.
</li>
<li><code>position: absolute</code> takes the pseudo element out of the flow of the document and positions it in relation to the parent.</li>
<li><code>width: 100%</code> ensures the element stretches the entire width of its parent.</li>
<li><code>height: 24px</code> is the same height as the shape.</li>
<li><code>bottom: 0</code> positions the pseudo element at the bottom of the parent.</li>
</ol>
<h4 data-type="Browser support">Browser support</h4>
<div>
<div class="snippet__browser-support">
98.0%
</div>
</div>
<p><span class="snippet__support-note">✅ No caveats.</span></p>
<ul>
<li>
<a href="https://caniuse.com/#feat=svg" target="_blank">https://caniuse.com/#feat=svg</a>
</li>
</ul>
</div>
<div class="snippet">
<h3 id="system-font-stack">System font stack</h3>
<p>Uses the native font of the operating system to get close to a native app feel.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;p class="system-font-stack"&gt;This text uses the system font.&lt;/p&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">.system-font-stack {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<div class="snippet-demo">
<p class="snippet-demo__system-font-stack">This text uses the system font.</p>
</div>
<style>
.snippet-demo__system-font-stack {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<p>The browser looks for each successive font, preferring the first one if possible, and falls back to the next of it cannot find the font (on the system or defined in CSS).</p>
<ol>
<li><code>-apple-system</code> is San Francisco, used on iOS and macOS (not Chrome however)</li>
<li><code>BlinkMacSystemFont</code> is San Francisco, used on macOS Chrome</li>
<li><code>Segoe UI</code> is used on Windows 10</li>
<li><code>Roboto</code> is used on Android</li>
<li><code>Oxygen-Sans</code> is used on GNU+Linux</li>
<li><code>Ubuntu</code> is used on Linux</li>
<li><code>"Helvetica Neue"</code> and <code>Helvetica</code> is used on macOS 10.10 and below (wrapped in quotes because it has a space)</li>
<li><code>Arial</code> is a font widely supported by all operating systems</li>
<li><code>sans-serif</code> is the fallback sans-serif font if none of the other fonts are supported</li>
</ol>
<h4 data-type="Browser support">Browser support</h4>
<div>
<div class="snippet__browser-support">
99+%
</div>
</div>
<p><span class="snippet__support-note">✅ No caveats.</span></p>
</div>
<div class="snippet">
<h3 id="triangle">Triangle</h3>
<p>Creates a triangle shape with pure CSS.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;div class="triangle"&gt;&lt;/div&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">.triangle {
width: 0;
height: 0;
border-top: 20px solid #333;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<div class="snippet-demo">
<div class="snippet-demo__triangles">
<div class="snippet-demo__triangle snippet-demo__triangle-1"></div>
<div class="snippet-demo__triangle snippet-demo__triangle-2"></div>
<div class="snippet-demo__triangle snippet-demo__triangle-3"></div>
<div class="snippet-demo__triangle snippet-demo__triangle-4"></div>
<div class="snippet-demo__triangle snippet-demo__triangle-5"></div>
</div>
</div>
<style>
.snippet-demo__triangles {
display: flex;
align-items: center;
}
.snippet-demo__triangle {
display: inline-block;
width: 0;
height: 0;
margin-right: 0.25rem;
}
.snippet-demo__triangle-1 {
border-top: 20px solid #333;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
}
.snippet-demo__triangle-2 {
border-bottom: 20px solid #333;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
}
.snippet-demo__triangle-3 {
border-left: 20px solid #333;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
}
.snippet-demo__triangle-4 {
border-right: 20px solid #333;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
}
.snippet-demo__triangle-5 {
border-top: 40px solid #333;
border-left: 15px solid transparent;
border-right: 15px solid transparent;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<p>
<a href="https://stackoverflow.com/q/7073484" target="_blank">View this link for a detailed explanation.</a>
</p>
<p>The color of the border is the color of the triangle. The side the triangle tip points corresponds to the opposite <code>border-*</code> property. For example, a color on <code>border-top</code> means the arrow points downward.</p>
<p>Experiment with the <code>px</code> values to change the proportion of the triangle.</p>
<h4 data-type="Browser support">Browser support</h4>
<div>
<div class="snippet__browser-support">
99+%
</div>
</div>
<p><span class="snippet__support-note">✅ No caveats.</span></p>
</div>
<div class="snippet">
<h3 id="truncate-text">Truncate text</h3>
<p>If the text is longer than one line, it will be truncated and end with an ellipsis <code>...</code>.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;p class="truncate-text"&gt;If I exceed one line's width, I will be truncated.&lt;/p&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">.truncate-text {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<div class="snippet-demo">
<p class="snippet-demo__truncate-text">
This text will be truncated if it exceeds 200px in width.
</p>
</div>
<style>
.snippet-demo__truncate-text {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 200px;
margin: 0;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<ol>
<li><code>overflow: hidden</code> prevents the text from overflowing its dimensions (for a block, 100% width and auto height).</li>
<li><code>white-space: nowrap</code> prevents the text from exceeding one line in height.</li>
<li><code>text-overflow: ellipsis</code> makes it so that if the text exceeds its dimensions, it will end with an ellipsis.</li>
</ol>
<h4 data-type="Browser support">Browser support</h4>
<div>
<div class="snippet__browser-support">
98.1%
</div>
</div>
<p><span class="snippet__support-note">✅ No caveats.</span></p>
<ul>
<li>
<a href="https://caniuse.com/#feat=text-overflow" target="_blank">https://caniuse.com/#feat=text-overflow</a>
</li>
</ul>
</div>
</div>
</main>
</div>
<footer class="footer"></footer>
</body>
</html>

11210
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

29
package.json Normal file
View File

@ -0,0 +1,29 @@
{
"name": "30-seconds-of-css",
"version": "0.0.0",
"description": "A curated collection of useful CSS snippets.",
"main": "index.js",
"scripts": {
"dev": "nodemon -e md,js ./scripts/build.js & parcel",
"build": "node ./scripts/build.js && npm run prettier && npm run parcel",
"parcel": "parcel build index.html --public-url ./",
"prettier": "prettier --single-quote --no-semi --print-width=100 ./js/index.js --write './src/**/*.js' './src/**/*.scss'"
},
"author": "atomiks",
"license": "MIT",
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-env": "^1.6.1",
"babel-preset-stage-2": "^6.24.1",
"caniuse-db": "^1.0.30000810",
"jsdom": "^11.6.2",
"marked": "^0.3.16",
"node-sass": "^4.7.2",
"nodemon": "^1.15.1",
"normalize.css": "^8.0.0",
"parcel-bundler": "^1.6.2",
"prettier": "^1.10.2",
"pretty": "^2.0.0",
"prismjs": "^1.11.0"
}
}

70
scripts/build.js Normal file
View File

@ -0,0 +1,70 @@
const fs = require('fs')
const path = require('path')
const marked = require('marked')
const pretty = require('pretty')
const { JSDOM } = require('jsdom')
const caniuseDb = require('caniuse-db/data.json')
const { toKebabCase, emptyHTML } = 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 SNIPPET_CONTAINER_SELECTOR = '.main > .container'
const createElement = str => {
const el = document.createElement('div')
el.innerHTML = str
return el.firstElementChild
}
const template = markdown => `
<div class="snippet">
${markdown}
</div>
`
const document = new JSDOM(fs.readFileSync('./index.html', 'utf8')).window.document
const snippetContainer = document.querySelector('.main > .container')
const sidebarLinks = document.querySelector('.sidebar__links')
emptyHTML(snippetContainer, sidebarLinks)
for (const snippetFile of fs.readdirSync(SNIPPETS_PATH)) {
const snippetPath = path.join(SNIPPETS_PATH, snippetFile)
const snippetData = fs.readFileSync(snippetPath, 'utf8')
const markdown = marked(snippetData, { renderer })
const snippetTemplate = template(markdown)
const el = createElement(snippetTemplate)
snippetContainer.append(el)
sidebarLinks.append(
createElement(
`<a class="sidebar__link" href="#${snippetFile.replace('.md', '')}">${
el.querySelector('h3').innerHTML
}</a>`
)
)
const featUsageShares = (snippetData.match(/https?:\/\/caniuse\.com\/#feat=.*/g) || []).map(
feat => {
const featData = caniuseDb.data[feat.match(/#feat=(.*)/)[1]]
return featData ? Number(featData.usage_perc_y + featData.usage_perc_a) : 100
}
)
el.querySelector('h4:last-of-type').after(
createElement(`
<div>
<div class="snippet__browser-support">
${featUsageShares.length ? Math.min(...featUsageShares).toPrecision(3) : '99+'}%
</div>
</div>
`)
)
}
fs.writeFileSync('./index.html', pretty(document.documentElement.outerHTML, { ocd: true }))

60
snippets/clearfix.md Normal file
View File

@ -0,0 +1,60 @@
### Clearfix
Ensures that an element self-clears its children.
#### HTML
```html
<div class="clearfix">
<div class="floated">float a</div>
<div class="floated">float b</div>
<div class="floated">float c</div>
</div>
```
#### CSS
```css
.clearfix::after {
content: '';
display: table;
clear: both;
}
.floated {
float: left;
}
```
#### Demo
<div class="snippet-demo">
<div class="snippet-demo__clearfix">
<div class="snippet-demo__floated">float a</div>
<div class="snippet-demo__floated">float b</div>
<div class="snippet-demo__floated">float c</div>
</div>
</div>
<style>
.snippet-demo__clearfix::after {
content: '';
display: table;
clear: both;
}
.snippet-demo__floated {
float: left;
}
</style>
#### Explanation
1. `.clearfix::after` defines a pseudo element.
2. `content: ''` allows the pseudo element to affect layout.
3. `clear: both` indicates that the left, right or both sides of the element cannot be adjacent
to earlier floated elements within the same block formatting context.
#### Browser support
<span class="snippet__support-note">✅ No caveats.</span>

View File

@ -0,0 +1,41 @@
### Custom text selection
Changes the styling of text selection.
#### HTML
```html
<p class="custom-text-selection">Select some of this text.</p>
```
#### CSS
```css
.text-selection::selection {
background: red;
color: white;
}
```
#### Demo
<div class="snippet-demo">
<p class="snippet-demo__custom-text-selection">Select some of this text.</p>
</div>
<style>
.snippet-demo__custom-text-selection::selection {
background: red;
color: white;
}
</style>
#### Explanation
`::selection` defines a pseudo selector on an element to style text within it when selected.
#### Browser support
<span class="snippet__support-note">⚠️ Requires prefixes for full support.</span>
* https://caniuse.com/#feat=css-selection

View File

@ -0,0 +1,106 @@
### Easing variables
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"></div>
```
#### CSS
```css
:root {
--ease-in-quad: cubic-bezier(0.55, 0.085, 0.68, 0.53);
--ease-in-cubic: cubic-bezier(0.55, 0.055, 0.675, 0.19);
--ease-in-quart: cubic-bezier(0.895, 0.03, 0.685, 0.22);
--ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06);
--ease-in-expo: cubic-bezier(0.95, 0.05, 0.795, 0.035);
--ease-in-circ: cubic-bezier(0.6, 0.04, 0.98, 0.335);
--ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94);
--ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1);
--ease-out-quart: cubic-bezier(0.165, 0.84, 0.44, 1);
--ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1);
--ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1);
--ease-out-circ: cubic-bezier(0.075, 0.82, 0.165, 1);
--ease-in-out-quad: cubic-bezier(0.455, 0.03, 0.515, 0.955);
--ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1);
--ease-in-out-quart: cubic-bezier(0.77, 0, 0.175, 1);
--ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1);
--ease-in-out-expo: cubic-bezier(1, 0, 0, 1);
--ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.easing-variables {
width: 50px;
height: 50px;
background: #333;
transition: transform 1s var(--ease-out-quart);
}
.easing-variables:hover {
transform: rotate(45deg);
}
```
#### Demo
<div class="snippet-demo">
<div class="snippet-demo__easing-variables">Hover</div>
</div>
<style>
:root {
--ease-in-quad: cubic-bezier(0.55, 0.085, 0.68, 0.53);
--ease-in-cubic: cubic-bezier(0.55, 0.055, 0.675, 0.19);
--ease-in-quart: cubic-bezier(0.895, 0.03, 0.685, 0.22);
--ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06);
--ease-in-expo: cubic-bezier(0.95, 0.05, 0.795, 0.035);
--ease-in-circ: cubic-bezier(0.6, 0.04, 0.98, 0.335);
--ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94);
--ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1);
--ease-out-quart: cubic-bezier(0.165, 0.84, 0.44, 1);
--ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1);
--ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1);
--ease-out-circ: cubic-bezier(0.075, 0.82, 0.165, 1);
--ease-in-out-quad: cubic-bezier(0.455, 0.03, 0.515, 0.955);
--ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1);
--ease-in-out-quart: cubic-bezier(0.77, 0, 0.175, 1);
--ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1);
--ease-in-out-expo: cubic-bezier(1, 0, 0, 1);
--ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.snippet-demo__easing-variables {
width: 75px;
height: 75px;
background: #333;
color: white;
font-size: 0.8rem;
font-weight: bold;
display: flex;
justify-content: center;
align-items: center;
transition: transform 1s var(--ease-out-quart);
}
.snippet-demo__easing-variables:hover {
transform: rotate(45deg);
}
</style>
#### Explanation
The variables are defined globally with 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.
#### Browser support
<span class="snippet__support-note">✅ No caveats.</span>
* https://caniuse.com/#feat=css-variables

51
snippets/etched-text.md Normal file
View File

@ -0,0 +1,51 @@
### Etched text
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;
font-size: 1.5rem;
font-weight: bold;
color: #b8bec5;
}
```
#### Demo
<div class="snippet-demo">
<p class="snippet-demo__etched-text">I appear etched into the background.</p>
</div>
<style>
.snippet-demo__etched-text {
font-size: 1.5rem;
font-weight: bold;
color: #b8bec5;
text-shadow: 0 2px 0 white;
}
</style>
#### Explanation
`text-shadow: 0 2px white` creates a white shadow offset `0px` horizontally and `2px` vertically
from the origin position.
The background must be darker than the shadow for the effect to work.
The text color should be slightly faded to make it look like it's engraved/carved out
of the background.
#### Browser support
<span class="snippet__support-note">✅ No caveats.</span>
* https://caniuse.com/#feat=css-textshadow

51
snippets/gradient-text.md Normal file
View File

@ -0,0 +1,51 @@
### Gradient text
Gives text a gradient color.
#### HTML
```html
<p class="gradient-text">Gradient text</p>
```
#### CSS
```css
.gradient-text {
background: -webkit-linear-gradient(pink, red);
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
}
```
#### Demo
<div class="snippet-demo">
<p class="snippet-demo__gradient-text">
Gradient text
</p>
</div>
<style>
.snippet-demo__gradient-text {
background: -webkit-linear-gradient(pink, red);
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
font-size: 2rem;
font-weight: bold;
margin: 0;
}
</style>
#### Explanation
1. `background: -webkit-linear-gradient(...)` gives the text element a gradient background.
2. `webkit-text-fill-color: transparent` fills the text with a transparent color.
3. `webkit-background-clip: text` clips the background with the text, filling the text with
the gradient background as the color.
#### Browser support
<span class="snippet__support-note">⚠️ Uses non-standard properties.</span>
* https://caniuse.com/#feat=text-stroke

View File

@ -0,0 +1,83 @@
### Hairline border
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;
}
@media (min-resolution: 2dppx) {
.hairline-border {
box-shadow: 0 0 0 0.5px;
}
}
@media (min-resolution: 3dppx) {
.hairline-border {
box-shadow: 0 0 0 0.33333333px;
}
}
@media (min-resolution: 4dppx) {
.hairline-border {
box-shadow: 0 0 0 0.25px;
}
}
```
#### Demo
<div class="snippet-demo">
<p class="snippet-demo__hairline-border">Text with a hairline border around it.</p>
</div>
<style>
.snippet-demo__hairline-border {
box-shadow: 0 0 0 1px;
}
@media (min-resolution: 2dppx) {
.snippet-demo__hairline-border {
box-shadow: 0 0 0 0.5px;
}
}
@media (min-resolution: 3dppx) {
.snippet-demo__hairline-border {
box-shadow: 0 0 0 0.33333333px;
}
}
@media (min-resolution: 4dppx) {
.snippet-demo__hairline-border {
box-shadow: 0 0 0 0.25px;
}
}
</style>
#### Explanation
1. `box-shadow`, when only using spread, adds a psuedo-border which can use subpixels\*.
2. Use `@media (min-resolution: ...)` to check the device pixel ratio (`1ddpx` equals 96 DPI),
setting the spread of the `box-shadow` equal to `1 / dppx`.
#### Browser Support
<span class="snippet__support-note">⚠️ Needs alternate syntax and JavaScript user agent checking for full support.</span>
* https://caniuse.com/#feat=css-boxshadow
* https://caniuse.com/#feat=css-media-resolution
<hr>
\*Chrome does not support subpixel values on `border`. Safari does not support subpixel values on `box-shadow`. Firefox supports subpixel values on both.

View File

@ -0,0 +1,50 @@
### Horizontal and vertical centering
Horizontally and vertically centers a child element within a parent element.
#### HTML
```html
<div class="horizontal-and-vertical-centering">
<div class="child"></div>
</div>
```
#### CSS
```css
.horizontal-and-vertical-centering {
display: flex;
justify-content: center;
align-items: center;
}
```
#### Demo
<div class="snippet-demo">
<div class="snippet-demo__horizontal-and-vertical-centering">
<p class="snippet-demo__horizontal-and-vertical-centering__child">Centered content.</p>
</div>
</div>
<style>
.snippet-demo__horizontal-and-vertical-centering {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}
</style>
#### Explanation
1. `display: flex` enables flexbox.
2. `justify-content: center` centers the child horizontally.
3. `align-items: center` centers the child vertically.
#### Browser support
<span class="snippet__support-note">⚠️ Needs prefixes for full support.</span>
* https://caniuse.com/#feat=flexbox

View File

@ -0,0 +1,135 @@
### Mouse cursor gradient tracking
A hover effect where the gradient follows the mouse cursor.
#### HTML
```html
<button class="mouse-cursor-gradient-tracking">
<span>Hover me</span>
</button>
```
#### CSS
```css
.mouse-cursor-gradient-tracking {
position: relative;
background: #2379f7;
padding: 0.5rem 1rem;
font-size: 1.2rem;
border: none;
color: white;
cursor: pointer;
outline: none;
overflow: hidden;
}
.mouse-cursor-gradient-tracking span {
position: relative;
}
.mouse-cursor-gradient-tracking::before {
--size: 0;
content: '';
position: absolute;
left: var(--x);
top: var(--y);
width: var(--size);
height: var(--size);
background: radial-gradient(circle closest-side, pink, transparent);
transform: translate(-50%, -50%);
transition: width .2s ease, height .2s ease;
}
.mouse-cursor-gradient-tracking:hover::before {
--size: 200px;
}
```
#### JavaScript
```js
var btn = document.querySelector('.mouse-cursor-gradient-tracking')
btn.onmousemove = function (e) {
var x = e.pageX - btn.offsetLeft
var y = e.pageY - btn.offsetTop
btn.style.setProperty('--x', x + 'px')
btn.style.setProperty('--y', y + 'px')
}
```
#### Demo
<div class="snippet-demo">
<button class="snippet-demo__mouse-cursor-gradient-tracking">
<span>Hover me</span>
</button>
</div>
<style>
.snippet-demo__mouse-cursor-gradient-tracking {
position: relative;
background: #2379f7;
padding: 0.5rem 1rem;
font-size: 1.2rem;
border: none;
color: white;
cursor: pointer;
outline: none;
overflow: hidden;
}
.snippet-demo__mouse-cursor-gradient-tracking span {
position: relative;
}
.snippet-demo__mouse-cursor-gradient-tracking::before {
--size: 0;
content: '';
position: absolute;
left: var(--x);
top: var(--y);
width: var(--size);
height: var(--size);
background: radial-gradient(circle closest-side, aqua, rgba(0,255,255,0.0001));
transform: translate(-50%, -50%);
transition: width .2s ease, height .2s ease;
}
.snippet-demo__mouse-cursor-gradient-tracking:hover::before {
--size: 200px;
}
</style>
<script>
(function () {
var btn = document.querySelector('.snippet-demo__mouse-cursor-gradient-tracking')
btn.onmousemove = function (e) {
var x = e.pageX - btn.offsetLeft - btn.offsetParent.offsetLeft
var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
btn.style.setProperty('--x', x + 'px')
btn.style.setProperty('--y', y + 'px')
}
})()
</script>
#### Explanation
_TODO_
**Note!**
If the element's parent has a positioning context (`position: relative`), you will need to subtract
its offsets as well.
```js
var x = e.pageX - btn.offsetLeft - btn.offsetParent.offsetLeft
var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
```
#### Browser support
<div class="snippet__requires-javascript">Requires JavaScript</div>
<span class="snippet__support-note">⚠️ Requires JavaScript.</span>
* https://caniuse.com/#feat=css-variables

View File

@ -0,0 +1,91 @@
### Overflow scroll gradient
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">
Content to be scrolled
</div>
</div>
```
#### CSS
```css
.overflow-scroll-gradient {
position: relative;
}
.overflow-scroll-gradient::after {
content: '';
position: absolute;
bottom: 0;
width: 300px;
height: 25px;
background: linear-gradient(rgba(255, 255, 255, 0.001), white); /* transparent keyword is broken in Safari */
}
.overflow-scroll-gradient__scroller {
overflow-y: scroll;
background: white;
width: 300px;
height: 250px;
line-height: 1.2;
text-align: center;
}
```
#### Demo
<div class="snippet-demo">
<div class="snippet-demo__overflow-scroll-gradient">
<div class="snippet-demo__overflow-scroll-gradient__scroller">
Content to be scrolled
</div>
</div>
</div>
<style>
.snippet-demo__overflow-scroll-gradient {
position: relative;
}
.snippet-demo__overflow-scroll-gradient::after {
content: '';
background: linear-gradient(rgba(255, 255, 255, 0.001), white);
position: absolute;
width: 300px;
height: 25px;
bottom: 0;
}
.snippet-demo__overflow-scroll-gradient__scroller {
overflow-y: scroll;
background: white;
width: 300px;
height: 250px;
line-height: 1.2;
text-align: center;
}
</style>
<script>
document.querySelector('.snippet-demo__overflow-scroll-gradient__scroller').innerHTML = 'content '.repeat(200)
</script>
#### Explanation
1. `position: relative` on the parent establishes a Cartesian positioning context for psuedo elements.
2. `::after` defines a pseudo element.
3. `background-image: linear-gradient(...)` adds a linear gradient that fades from transparent to white
(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.
5. `width: 300px` matches the size of the scrolling element (which is a child of the parent that has
the pseudo element).
6. `height: 25px` is the height of the fading gradient psuedo element, which should be kept relatively small.
7. `bottom: 0` positions the pseudo element at the bottom of the parent.
#### Browser support
<span class="snippet__support-note">✅ No caveats.</span>
* https://caniuse.com/#feat=css-gradients

77
snippets/popout-menu.md Normal file
View File

@ -0,0 +1,77 @@
### Popout menu
Reveals an interactive popout menu on hover.
#### HTML
```html
<div class="reference">
<div class="popout-menu">
Popout menu
</div>
</div>
```
#### CSS
```css
.reference {
position: relative;
}
.popout-menu {
position: absolute;
visibility: hidden;
left: 100%;
}
.reference:hover > .popout-menu {
visibility: visible;
}
```
#### Demo
<div class="snippet-demo">
<div class="snippet-demo__reference">
<div class="snippet-demo__popout-menu">
Popout menu
</div>
</div>
</div>
<style>
.snippet-demo__reference {
background: linear-gradient(135deg, #ff4c9f, #ff7b74);
height: 75px;
width: 75px;
position: relative;
will-change: transform;
}
.snippet-demo__popout-menu {
position: absolute;
visibility: hidden;
left: 100%;
background: #333;
color: white;
font-size: 0.9rem;
padding: 0.4rem 0.8rem;
width: 100px;
text-align: center;
}
.snippet-demo__reference:hover > .snippet-demo__popout-menu {
visibility: visible;
}
</style>
#### Explanation
1. `position: relative` on the reference parent establishes a Cartesian positioning context for its child.
2. `position: absolute` takes the popout menu out of the flow of the document and positions it
in relation to the parent.
3. `left: 100%` moves the the popout menu 100% of its parent's width from the left.
4. `visibility: hidden` hides the popout menu initially and allows for transitions (unlike `display: none`).
5. `.reference:hover > .popout-menu` means that when `.reference` is hovered over, select immediate
children with a class of `.popout-menu` and change their `visibility` to `visible`, which shows the popout.
#### Browser support
<span class="snippet__support-note">✅ No caveats.</span>

View File

@ -0,0 +1,75 @@
### Pretty text underline
A nicer alternative to `text-decoration: underline` 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;
font-size: 1.25rem;
text-shadow: 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-position: 0 1.04em;
background-repeat: repeat-x;
background-size: 1px 1px;
}
.pretty-text-underline::selection {
background-color: rgba(0, 150, 255, 0.3);
text-shadow: none;
}
```
#### Demo
<div class="snippet-demo">
<p class="snippet-demo__pretty-text-underline">Pretty text underline without clipping descending letters.</p>
</div>
<style>
.snippet-demo__pretty-text-underline {
display: inline;
font-size: 18px !important;
text-shadow: 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-position: 0 1.04em;
background-repeat: repeat-x;
background-size: 1px 1px;
}
.snippet-demo__pretty-text-underline::selection {
background-color: rgba(0, 150, 255, 0.3);
text-shadow: none;
}
</style>
#### Explanation
1. `text-shadow: ...` has 4 values with offsets that cover a 4x4 px area to ensure the underline
has a "thick" shadow that covers the line where descenders clip it. Use a color
that matches the background. For a larger font, use a larger `px` size.
2. `background-image: linear-gradient(...)` creates a 90deg gradient with the current
text color (`currentColor`).
3. The `background-*` properties size the gradient as 1x1px at the bottom and repeats it along the x-axis.
4. The `::selection` pseudo selector ensures the text shadow does not interfere with text
selection.
#### Browser support
<span class="snippet__support-note">✅ No caveats.</span>
* https://caniuse.com/#feat=css-textshadow
* https://caniuse.com/#feat=css-gradients

View File

@ -0,0 +1,66 @@
### Shape separator
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;
height: 48px;
}
.shape-separator::after {
content: '';
background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxLjQxNCI+PHBhdGggZD0iTTEyIDEybDEyIDEySDBsMTItMTJ6IiBmaWxsPSIjZmZmIi8+PC9zdmc+);
position: absolute;
width: 100%;
height: 24px;
bottom: 0;
}
```
#### Demo
<div class="snippet-demo is-distinct">
<div class="snippet-demo__shape-separator"></div>
</div>
<style>
.snippet-demo__shape-separator {
position: relative;
height: 48px;
margin: -0.75rem -1.25rem;
}
.snippet-demo__shape-separator::after {
content: '';
background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxLjQxNCI+PHBhdGggZD0iTTEyIDEybDEyIDEySDBsMTItMTJ6IiBmaWxsPSIjZmZmIi8+PC9zdmc+);
position: absolute;
width: 100%;
height: 24px;
bottom: 0;
}
</style>
#### Explanation
1. `position: relative` on the element establishes a Cartesian positioning context for psuedo elements.
2. `::after` defines a pseudo element.
3. `background-image: url(...)` adds the SVG shape (a 24x24 triangle in base64 format) as the background image
of the psuedo element, which repeats by default. It must be the same color as the block that is being
separated.
4. `position: absolute` takes the pseudo element out of the flow of the document and positions it in relation to the parent.
5. `width: 100%` ensures the element stretches the entire width of its parent.
6. `height: 24px` is the same height as the shape.
7. `bottom: 0` positions the pseudo element at the bottom of the parent.
#### Browser support
<span class="snippet__support-note">✅ No caveats.</span>
* https://caniuse.com/#feat=svg

View File

@ -0,0 +1,48 @@
### System font stack
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, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
```
#### Demo
<div class="snippet-demo">
<p class="snippet-demo__system-font-stack">This text uses the system font.</p>
</div>
<style>
.snippet-demo__system-font-stack {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
</style>
#### Explanation
The browser looks for each successive font, preferring the first one if possible, and
falls back to the next of it cannot find the font (on the system or defined in CSS).
1. `-apple-system` is San Francisco, used on iOS and macOS (not Chrome however)
2. `BlinkMacSystemFont` is San Francisco, used on macOS Chrome
3. `Segoe UI` is used on Windows 10
4. `Roboto` is used on Android
5. `Oxygen-Sans` is used on GNU+Linux
6. `Ubuntu` is used on Linux
7. `"Helvetica Neue"` and `Helvetica` is used on macOS 10.10 and below (wrapped in quotes because it has a space)
8. `Arial` is a font widely supported by all operating systems
9. `sans-serif` is the fallback sans-serif font if none of the other fonts are supported
#### Browser support
<span class="snippet__support-note">✅ No caveats.</span>

91
snippets/triangle.md Normal file
View File

@ -0,0 +1,91 @@
### Triangle
Creates a triangle shape with pure CSS.
#### HTML
```html
<div class="triangle"></div>
```
#### CSS
```css
.triangle {
width: 0;
height: 0;
border-top: 20px solid #333;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
}
```
#### Demo
<div class="snippet-demo">
<div class="snippet-demo__triangles">
<div class="snippet-demo__triangle snippet-demo__triangle-1"></div>
<div class="snippet-demo__triangle snippet-demo__triangle-2"></div>
<div class="snippet-demo__triangle snippet-demo__triangle-3"></div>
<div class="snippet-demo__triangle snippet-demo__triangle-4"></div>
<div class="snippet-demo__triangle snippet-demo__triangle-5"></div>
</div>
</div>
<style>
.snippet-demo__triangles {
display: flex;
align-items: center;
}
.snippet-demo__triangle {
display: inline-block;
width: 0;
height: 0;
margin-right: 0.25rem;
}
.snippet-demo__triangle-1 {
border-top: 20px solid #333;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
}
.snippet-demo__triangle-2 {
border-bottom: 20px solid #333;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
}
.snippet-demo__triangle-3 {
border-left: 20px solid #333;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
}
.snippet-demo__triangle-4 {
border-right: 20px solid #333;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
}
.snippet-demo__triangle-5 {
border-top: 40px solid #333;
border-left: 15px solid transparent;
border-right: 15px solid transparent;
}
</style>
#### Explanation
[View this link for a detailed explanation.](https://stackoverflow.com/q/7073484)
The color of the border is the color of the triangle. The side the triangle tip points
corresponds to the opposite `border-*` property. For example, a color on `border-top`
means the arrow points downward.
Experiment with the `px` values to change the proportion of the triangle.
#### Browser support
<span class="snippet__support-note">✅ No caveats.</span>

51
snippets/truncate-text.md Normal file
View File

@ -0,0 +1,51 @@
### Truncate text
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;
white-space: nowrap;
text-overflow: ellipsis;
}
```
#### Demo
<div class="snippet-demo">
<p class="snippet-demo__truncate-text">
This text will be truncated if it exceeds 200px in width.
</p>
</div>
<style>
.snippet-demo__truncate-text {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 200px;
margin: 0;
}
</style>
#### Explanation
1. `overflow: hidden` prevents the text from overflowing its dimensions
(for a block, 100% width and auto height).
2. `white-space: nowrap` prevents the text from exceeding one line in height.
3. `text-overflow: ellipsis` makes it so that if the text exceeds its dimensions, it
will end with an ellipsis.
#### Browser support
<span class="snippet__support-note">✅ No caveats.</span>
* https://caniuse.com/#feat=text-overflow

View File

@ -0,0 +1,39 @@
.back-to-top-button {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
font-size: 2rem;
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;
&: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;
}
&.is-visible {
visibility: visible;
opacity: 1;
}
}

View File

@ -0,0 +1,60 @@
html {
font-size: 0.95rem;
}
body {
font-family: -apple-system, BlinkMacSystemFont, Roboto, Segoe UI, 'Helvetica Neue', Helvetica,
Arial, sans-serif;
background: #f6f7fd;
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: 1000px;
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: 200px;
}
}

View File

@ -0,0 +1,927 @@
/*!
* 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);
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,104 @@
.sidebar {
background: #202e4e;
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: #202e4e;
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;
max-height: 378px;
margin-top: 44px;
box-shadow: 0 0.25rem 0.5rem -0.1rem rgba(0, 32, 128, 0.2);
&.is-active {
transform: rotateX(0);
visibility: visible;
opacity: 1;
}
}
&__link {
display: block;
color: #e3f5ff;
padding: 0.5rem 0.75rem;
transition: all 0.1s ease-out;
border-left: 2px solid #576a85;
margin: 0.5rem;
font-weight: 500;
font-size: 0.95rem;
&:hover {
color: #88f4ff;
background: rgba(255, 255, 255, 0.1);
border-color: pink;
}
}
}
@media (min-width: 992px) {
.sidebar {
left: 0;
top: 0;
bottom: 0;
width: 15%;
max-width: 250px;
min-width: 200px;
height: 100%;
background: linear-gradient(-30deg, #2a3d67, #14264e);
box-shadow: 0.4rem 0.4rem 0.8rem rgba(0, 32, 64, 0.1);
overflow-y: auto;
color: white;
&::-webkit-scrollbar-track {
background-color: rgba(0, 0, 0, 0.6);
}
&::-webkit-scrollbar {
width: 10px;
background-color: #4b6191;
}
&::-webkit-scrollbar-thumb {
background-color: #4b6191;
}
&__links {
background: none;
box-shadow: none;
visibility: visible;
opacity: 1;
transform: rotateX(0);
margin-top: 0;
max-height: none;
}
&__menu {
display: none;
}
}
}

View File

@ -0,0 +1,128 @@
.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;
}
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;
line-height: 2;
padding: 0 0.5rem;
border-radius: 3px;
font-size: 0.9rem;
text-transform: uppercase;
background: #333;
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);
&[data-type='HTML'] {
color: white;
border: none;
background: linear-gradient(135deg, #ff4c9f, #ff7b74);
}
&[data-type='CSS'] {
color: white;
border: none;
background: linear-gradient(135deg, #7983ff, #5f9de9);
}
&[data-type='JavaScript'] {
color: white;
border: none;
background: 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;
}
}
.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;
}
}
}

146
src/css/deps/prism.css Normal file
View File

@ -0,0 +1,146 @@
code[class*='language-'],
pre[class*='language-'] {
color: rgb(50, 75, 100);
background: none;
font-family: 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: #f5f6f9;
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: #da7800;
}
.token.punctuation {
color: #93a0c7;
}
.namespace {
opacity: 0.7;
}
.token.tag {
color: #e22f70;
}
.token.property,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #0087ca;
}
.token.string,
.language-css .token.string,
.token.url,
.token.attr-value,
.token.char,
.token.builtin,
.token.inserted {
color: #008a7e;
}
.token.operator,
.token.entity,
.style .token.string {
color: #f53737;
}
.token.important,
.token.atrule,
.token.keyword {
color: #7552ff;
}
.token.function {
color: #396dff;
}
.token.regex,
.token.variable {
color: #00a8d4;
}
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

6
src/css/index.scss Normal file
View File

@ -0,0 +1,6 @@
@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';

BIN
src/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -0,0 +1,14 @@
import jump from '../deps/jump'
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
})
}

34
src/js/components/Menu.js Normal file
View File

@ -0,0 +1,34 @@
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 = () => [menu, links].forEach(el => el.classList.toggle(ACTIVE_CLASS))
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 }

196
src/js/deps/jump.js Normal file
View File

@ -0,0 +1,196 @@
;(function(global, factory) {
typeof exports === 'object' && typeof module !== 'undefined'
? (module.exports = factory())
: typeof define === 'function' && define.amd ? define(factory) : (global.Jump = factory())
})(this, function() {
'use strict'
// Robert Penner's easeInOutQuad
// find the rest of his easing functions here: http://robertpenner.com/easing/
// find them exported for ES6 consumption here: https://github.com/jaxgeller/ez.js
var easeInOutQuad = function easeInOutQuad(t, b, c, d) {
t /= d / 2
if (t < 1) return c / 2 * t * t + b
t--
return -c / 2 * (t * (t - 2) - 1) + b
}
var _typeof =
typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol'
? function(obj) {
return typeof obj
}
: function(obj) {
return obj &&
typeof Symbol === 'function' &&
obj.constructor === Symbol &&
obj !== Symbol.prototype
? 'symbol'
: typeof obj
}
var jumper = function jumper() {
// private variable cache
// no variables are created during a jump, preventing memory leaks
var element = void 0 // element to scroll to (node)
var start = void 0 // where scroll starts (px)
var stop = void 0 // where scroll stops (px)
var offset = void 0 // adjustment from the stop position (px)
var easing = void 0 // easing function (function)
var a11y = void 0 // accessibility support flag (boolean)
var distance = void 0 // distance of scroll (px)
var duration = void 0 // scroll duration (ms)
var timeStart = void 0 // time scroll started (ms)
var timeElapsed = void 0 // time spent scrolling thus far (ms)
var next = void 0 // next scroll position (px)
var callback = void 0 // to call when done scrolling (function)
// scroll position helper
function location() {
return window.scrollY || window.pageYOffset
}
// element offset helper
function top(element) {
return element.getBoundingClientRect().top + start
}
// rAF loop helper
function loop(timeCurrent) {
// store time scroll started, if not started already
if (!timeStart) {
timeStart = timeCurrent
}
// determine time spent scrolling so far
timeElapsed = timeCurrent - timeStart
// calculate next scroll position
next = easing(timeElapsed, start, distance, duration)
// scroll to it
window.scrollTo(0, next)
// check progress
timeElapsed < duration
? window.requestAnimationFrame(loop) // continue scroll loop
: done() // scrolling is done
}
// scroll finished helper
function done() {
// account for rAF time rounding inaccuracies
window.scrollTo(0, start + distance)
// if scrolling to an element, and accessibility is enabled
if (element && a11y) {
// add tabindex indicating programmatic focus
element.setAttribute('tabindex', '-1')
// focus the element
element.focus()
}
// if it exists, fire the callback
if (typeof callback === 'function') {
callback()
}
// reset time for next jump
timeStart = false
}
// API
function jump(target) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}
// resolve options, or use defaults
duration = options.duration || 1000
offset = options.offset || 0
callback = options.callback // "undefined" is a suitable default, and won't be called
easing = options.easing || easeInOutQuad
a11y = options.a11y || false
// cache starting position
start = location()
// resolve target
switch (typeof target === 'undefined' ? 'undefined' : _typeof(target)) {
// scroll from current position
case 'number':
element = undefined // no element to scroll to
a11y = false // make sure accessibility is off
stop = start + target
break
// scroll to element (node)
// bounding rect is relative to the viewport
case 'object':
element = target
stop = top(element)
break
// scroll to element (selector)
// bounding rect is relative to the viewport
case 'string':
element = document.querySelector(target)
stop = top(element)
break
}
// resolve scroll distance, accounting for offset
distance = stop - start + offset
// resolve duration
switch (_typeof(options.duration)) {
// number in ms
case 'number':
duration = options.duration
break
// function passed the distance of the scroll
case 'function':
duration = options.duration(distance)
break
}
// start the loop
window.requestAnimationFrame(loop)
}
// expose only the jump method
return jump
}
// export singleton
var singleton = jumper()
return (() => {
let scrolling
const end = () => (scrolling = false)
return (to, options = {}) => {
if (scrolling) return
const scrollY = window.scrollY || window.pageYOffset
if (to !== '.header') location.hash = to
scroll(0, scrollY)
scrolling = true
setTimeout(end, options.duration || 0)
return singleton(to, options)
}
})()
})

16
src/js/deps/polyfills.js Normal file
View File

@ -0,0 +1,16 @@
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
}
}

40
src/js/deps/utils.js Normal file
View File

@ -0,0 +1,40 @@
export const select = s => document.querySelector(s)
export const selectAll = s => [].slice.call(document.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
/*
* 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'
}
})
}
})()

14
src/js/index.js Normal file
View File

@ -0,0 +1,14 @@
// Deps
import 'normalize.css'
import 'prismjs'
// CSS
import '../css/deps/prism.css'
import '../css/index.scss'
// Polyfills
import './deps/polyfills'
// Components
import Menu from './components/Menu'
import BackToTopButton from './components/BackToTopButton'

8
utils/utils.js Normal file
View File

@ -0,0 +1,8 @@
exports.toKebabCase = 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)
.map(x => x.toLowerCase())
.join('-');
exports.emptyHTML = (...els) => els.forEach(el => (el.innerHTML = ''))