This commit is contained in:
atomiks
2018-03-03 15:05:35 +10:00
parent 6d9560aad8
commit 01ec0a8a83
46 changed files with 644 additions and 418 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -14,32 +14,44 @@
<nav class="sidebar" aria-label="Table of Contents">
<button class="hamburger hamburger--spin sidebar__menu" type="button" aria-label="Menu" aria-expanded="false">
<span class="hamburger-box">
<span class="hamburger-inner"></span>
<span class="hamburger-inner"></span>
</span>
</button>
<div class="sidebar__links">
<a class="sidebar__link" href="#box-sizing-reset">Box-sizing reset</a>
<a class="sidebar__link" href="#clearfix">Clearfix</a>
<a class="sidebar__link" href="#constant-width-to-height-ratio">Constant width to height ratio</a>
<a class="sidebar__link" href="#custom-scrollbar">Custom scrollbar</a>
<a class="sidebar__link" href="#custom-text-selection">Custom text selection</a>
<a class="sidebar__link" href="#disable-selection">Disable selection</a>
<a class="sidebar__link" href="#donut-spinner">Donut spinner</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="#evenly-distributed-children">Evenly distributed children</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="#hover-underline-animation">Hover underline animation</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>
<section data-type="layout" class="sidebar__section">
<h4 class="sidebar__section-heading">layout</h4>
<a class="sidebar__link" href="#box-sizing-reset"><span>Box-sizing reset</span></a>
<a class="sidebar__link" href="#clearfix"><span>Clearfix</span></a>
<a class="sidebar__link" href="#constant-width-to-height-ratio"><span>Constant width to height ratio</span></a>
<a class="sidebar__link" href="#evenly-distributed-children"><span>Evenly distributed children</span></a>
<a class="sidebar__link" href="#horizontal-and-vertical-centering"><span>Horizontal and vertical centering</span></a>
<a class="sidebar__link" href="#truncate-text"><span>Truncate text</span></a>
</section>
<section data-type="visual" class="sidebar__section">
<h4 class="sidebar__section-heading">visual</h4>
<a class="sidebar__link" href="#custom-scrollbar"><span>Custom scrollbar</span></a>
<a class="sidebar__link" href="#custom-text-selection"><span>Custom text selection</span></a>
<a class="sidebar__link" href="#etched-text"><span>Etched text</span></a>
<a class="sidebar__link" href="#gradient-text"><span>Gradient text</span></a>
<a class="sidebar__link" href="#hairline-border"><span>Hairline border</span></a>
<a class="sidebar__link" href="#overflow-scroll-gradient"><span>Overflow scroll gradient</span></a>
<a class="sidebar__link" href="#pretty-text-underline"><span>Pretty text underline</span></a>
<a class="sidebar__link" href="#shape-separator"><span>Shape separator</span></a>
<a class="sidebar__link" href="#system-font-stack"><span>System font stack</span></a>
<a class="sidebar__link" href="#triangle"><span>Triangle</span></a>
</section>
<section data-type="animation" class="sidebar__section">
<h4 class="sidebar__section-heading">animation</h4>
<a class="sidebar__link" href="#donut-spinner"><span>Donut spinner</span></a>
<a class="sidebar__link" href="#easing-variables"><span>Easing variables</span></a>
<a class="sidebar__link" href="#hover-underline-animation"><span>Hover underline animation</span></a>
</section>
<section data-type="interactivity" class="sidebar__section">
<h4 class="sidebar__section-heading">interactivity</h4>
<a class="sidebar__link" href="#disable-selection"><span>Disable selection</span></a>
<a class="sidebar__link" href="#mouse-cursor-gradient-tracking"><span>Mouse cursor gradient tracking</span></a>
<a class="sidebar__link" href="#popout-menu"><span>Popout menu</span></a>
</section>
</div>
</nav>
<div class="content-wrapper">
@ -55,8 +67,20 @@
</header>
<main class="main" id="main">
<div class="container">
<nav class="tags" aria-label="Filter by tags">
<button class="tags__tag is-large is-active" data-type="all">
<i data-feather="check"></i>all</button>
<button class="tags__tag is-large " data-type="layout">
<i data-feather="layout"></i>layout</button>
<button class="tags__tag is-large " data-type="visual">
<i data-feather="eye"></i>visual</button>
<button class="tags__tag is-large " data-type="animation">
<i data-feather="loader"></i>animation</button>
<button class="tags__tag is-large " data-type="interactivity">
<i data-feather="edit-2"></i>interactivity</button>
</nav>
<div class="snippet">
<h3 id="box-sizing-reset">Box-sizing reset</h3>
<h3 id="box-sizing-reset"><span>Box-sizing reset</span><span class="tags__tag snippet__tag" data-type="layout"><i data-feather="layout"></i>layout</span></h3>
<p>Resets the box-model so that <code>width</code>s and <code>height</code>s are not affected by their <code>border</code>s or <code>padding</code>.</p>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">html {
box-sizing: border-box;
@ -67,7 +91,7 @@
box-sizing: inherit;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<div class="snippet-demo__box-sizing-reset">Demo</div>
</div>
@ -82,12 +106,12 @@
border: 5px solid;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>Explanation</h4>
<ol>
<li><code>box-sizing: border-box</code> makes the addition of <code>padding</code> or <code>border</code>s not affect an element's <code>width</code> or <code>height</code>.</li>
<li><code>box-sizing: inherit</code> makes an element respect its parent's <code>box-sizing</code> rule.</li>
</ol>
<h4 data-type="Browser support">Browser support</h4>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
98.2%
@ -99,11 +123,13 @@
<a href="https://caniuse.com/#feat=css3-boxsizing" target="_blank">https://caniuse.com/#feat=css3-boxsizing</a>
</li>
</ul>
<!-- tags: layout -->
</div>
<div class="snippet">
<h3 id="clearfix">Clearfix</h3>
<h3 id="clearfix"><span>Clearfix</span><span class="tags__tag snippet__tag" data-type="layout"><i data-feather="layout"></i>layout</span></h3>
<p>Ensures that an element self-clears its children.</p>
<h6 data-type="Note: This is only useful if you are still using float to build layouts. Please consider using a modern approach with flexbox layout or grid layout.">Note: This is only useful if you are still using float to build layouts. Please consider using a modern approach with flexbox layout or grid layout.</h6>
<h6>Note: This is only useful if you are still using float to build layouts. Please consider using a modern approach with flexbox layout or grid layout.</h6>
<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;
@ -111,7 +137,7 @@
&lt;/div&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">.clearfix::after {
content: "";
content: '';
display: block;
clear: both;
}
@ -119,7 +145,7 @@
float: left;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<div class="snippet-demo__clearfix">
<div class="snippet-demo__floated">float a</div>
@ -137,22 +163,24 @@
float: left;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>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>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
99+%
</div>
</div>
<p><span class="snippet__support-note">✅ No caveats.</span></p>
<!-- tags: layout -->
</div>
<div class="snippet">
<h3 id="constant-width-to-height-ratio">Constant width to height ratio</h3>
<h3 id="constant-width-to-height-ratio"><span>Constant width to height ratio</span><span class="tags__tag snippet__tag" data-type="layout"><i data-feather="layout"></i>layout</span></h3>
<p>Given an element of variable width, it will ensure its height remains proportionate in a responsive fashion (i.e., its width to height ratio remains constant).</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;div class="constant-width-to-height-ratio"&gt;&lt;/div&gt;
</code></pre>
@ -162,7 +190,7 @@
padding-top: 50%;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<p>Resize your browser window to see the proportion of the element remain the same.</p>
<div class="snippet-demo">
<div class="snippet-demo__constant-width-to-height-ratio"></div>
@ -174,19 +202,21 @@
padding-top: 50%;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>Explanation</h4>
<p><code>padding-top</code> and <code>padding-bottom</code> can be used as an alternative to <code>height</code> such that the percentage value causes an element's height to become a percentage of its parent's width, i.e. <code>50%</code> means
the height will be 50% of the parent element's width, which means it acts the same as <code>width</code>. This allows its proportion to remain constant.</p>
<h4 data-type="Browser support">Browser support</h4>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
99+%
</div>
</div>
<p><span class="snippet__support-note">⚠️ <code>padding-top</code> pushes any content within the element to the bottom.</span></p>
<!-- tags: layout -->
</div>
<div class="snippet">
<h3 id="custom-scrollbar">Custom scrollbar</h3>
<h3 id="custom-scrollbar"><span>Custom scrollbar</span><span class="tags__tag snippet__tag" data-type="visual"><i data-feather="eye"></i>visual</span></h3>
<p>Customizes the scrollbar style for the document and elements with scrollable overflow, on WebKit platforms.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;div class="custom-scrollbar"&gt;
&lt;p&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure id exercitationem nulla qui repellat laborum vitae, molestias tempora velit natus. Quas, assumenda nisi. Quisquam enim qui iure, consequatur velit sit?&lt;/p&gt;
@ -197,18 +227,18 @@
width: 8px;
}
::-webkit-scrollbar-track {
box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
box-shadow: inset 0 0 6px rgba(0,0,0,0.5);
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.5);
}
/* Scrollable element */
.some-element::webkit-scrollbar {
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<div class="snippet-demo__custom-scrollbar">
<p>
@ -236,7 +266,7 @@
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.5);
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>Explanation</h4>
<ol>
<li><code>::-webkit-scrollbar</code> targets the whole scrollbar element.</li>
<li><code>::-webkit-scrollbar-track</code> targets only the scrollbar track.</li>
@ -245,7 +275,7 @@
<p>There are many other pseudo-elements that you can use to style scrollbars. For more info, visit the
<a href="https://webkit.org/blog/363/styling-scrollbars/" target="_blank">WebKit Blog</a>
</p>
<h4 data-type="Browser support">Browser support</h4>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
87.3%
@ -257,9 +287,11 @@
<a href="https://caniuse.com/#feat=css-scrollbar" target="_blank">https://caniuse.com/#feat=css-scrollbar</a>
</li>
</ul>
<!-- tags: visual -->
</div>
<div class="snippet">
<h3 id="custom-text-selection">Custom text selection</h3>
<h3 id="custom-text-selection"><span>Custom text selection</span><span class="tags__tag snippet__tag" data-type="visual"><i data-feather="eye"></i>visual</span></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>
@ -272,7 +304,7 @@
color: white;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<p class="snippet-demo__custom-text-selection">Select some of this text.</p>
</div>
@ -286,9 +318,9 @@
color: white;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>Explanation</h4>
<p><code>::selection</code> defines a pseudo selector on an element to style text within it when selected. Note that if you don't combine any other selector your style will be applied at document root level, to any selectable element.</p>
<h4 data-type="Browser support">Browser support</h4>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
84.6%
@ -301,9 +333,11 @@ in any specification.</span></p>
<a href="https://caniuse.com/#feat=css-selection" target="_blank">https://caniuse.com/#feat=css-selection</a>
</li>
</ul>
<!-- tags: visual -->
</div>
<div class="snippet">
<h3 id="disable-selection">Disable selection</h3>
<h3 id="disable-selection"><span>Disable selection</span><span class="tags__tag snippet__tag" data-type="interactivity"><i data-feather="edit-2"></i>interactivity</span></h3>
<p>Makes the content unselectable.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;p&gt;You can select me.&lt;/p&gt;
&lt;p class="unselectable"&gt;You can't select me!&lt;/p&gt;
@ -312,7 +346,7 @@ in any specification.</span></p>
user-select: none;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<p>You can select me.</p>
<p class="snippet-demo__disable-selection">You can't select me!</p>
@ -322,9 +356,9 @@ in any specification.</span></p>
user-select: none;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>Explanation</h4>
<p><code>user-select: none</code> specifies that the text cannot be selected.</p>
<h4 data-type="Browser support">Browser support</h4>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
86.3%
@ -336,15 +370,21 @@ in any specification.</span></p>
<a href="https://caniuse.com/#feat=user-select-none" target="_blank">https://caniuse.com/#feat=user-select-none</a>
</li>
</ul>
<!-- tags: interactivity -->
</div>
<div class="snippet">
<h3 id="donut-spinner">Donut spinner</h3>
<h3 id="donut-spinner"><span>Donut spinner</span><span class="tags__tag snippet__tag" data-type="animation"><i data-feather="loader"></i>animation</span></h3>
<p>Creates a donut spinner that can be used to indicate the loading of content.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;div class="donut"&gt;&lt;/div&gt;
</code></pre>
<h4 data-type="CSS">CSS</h4><pre><code class="lang-css">@keyframes donut-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.donut {
display: inline-block;
@ -356,7 +396,7 @@ in any specification.</span></p>
animation: donut-spin 1.2s linear infinite;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<div class="snippet-demo__donut-spinner"></div>
</div>
@ -379,9 +419,9 @@ in any specification.</span></p>
animation: snippet-demo__donut-spin 1.2s linear infinite;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>Explanation</h4>
<p>Use a semi-transparent <code>border</code> for the whole element, except one side that will serve as the loading indicator for the donut. Use <code>animation</code> to rotate the element.</p>
<h4 data-type="Browser support">Browser support</h4>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
94.8%
@ -396,9 +436,11 @@ in any specification.</span></p>
<a href="https://caniuse.com/#feat=transforms2d" target="_blank">https://caniuse.com/#feat=transforms2d</a>
</li>
</ul>
<!-- tags: animation -->
</div>
<div class="snippet">
<h3 id="easing-variables">Easing variables</h3>
<h3 id="easing-variables"><span>Easing variables</span><span class="tags__tag snippet__tag" data-type="animation"><i data-feather="loader"></i>animation</span></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>
@ -432,7 +474,7 @@ in any specification.</span></p>
transform: rotate(45deg);
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<div class="snippet-demo__easing-variables">Hover</div>
</div>
@ -473,10 +515,10 @@ in any specification.</span></p>
transform: rotate(45deg);
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>Explanation</h4>
<p>The variables are defined globally within 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 <code>html</code>, except that its specificity is higher.</p>
<h4 data-type="Browser support">Browser support</h4>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
87.2%
@ -488,9 +530,11 @@ in any specification.</span></p>
<a href="https://caniuse.com/#feat=css-variables" target="_blank">https://caniuse.com/#feat=css-variables</a>
</li>
</ul>
<!-- tags: animation -->
</div>
<div class="snippet">
<h3 id="etched-text">Etched text</h3>
<h3 id="etched-text"><span>Etched text</span><span class="tags__tag snippet__tag" data-type="visual"><i data-feather="eye"></i>visual</span></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>
@ -501,7 +545,7 @@ in any specification.</span></p>
color: #b8bec5;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<p class="snippet-demo__etched-text">I appear etched into the background.</p>
</div>
@ -513,11 +557,11 @@ in any specification.</span></p>
text-shadow: 0 2px 0 white;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>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>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
97.9%
@ -529,9 +573,11 @@ in any specification.</span></p>
<a href="https://caniuse.com/#feat=css-textshadow" target="_blank">https://caniuse.com/#feat=css-textshadow</a>
</li>
</ul>
<!-- tags: visual -->
</div>
<div class="snippet">
<h3 id="evenly-distributed-children">Evenly distributed children</h3>
<h3 id="evenly-distributed-children"><span>Evenly distributed children</span><span class="tags__tag snippet__tag" data-type="layout"><i data-feather="layout"></i>layout</span></h3>
<p>Evenly distributes child elements within a parent element.</p>
<h4 data-type="HTML">HTML</h4><pre><code class="lang-html">&lt;div class="evenly-distributed-children"&gt;
&lt;p&gt;Item1&lt;/p&gt;
@ -544,7 +590,7 @@ in any specification.</span></p>
justify-content: space-between;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<div class="snippet-demo__evenly-distributed-children">
<p>Item1</p>
@ -559,13 +605,13 @@ in any specification.</span></p>
justify-content: space-between;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>Explanation</h4>
<ol>
<li><code>display: flex</code> enables flexbox.</li>
<li><code>justify-content: space-between</code> evenly distributes child elements horizontally. The first item is positioned at the left edge, while the last item is positioned at the right edge.</li>
</ol>
<p>Alternatively, use <code>justify-content: space-around</code> to distribute the children with space around them, rather than between them.</p>
<h4 data-type="Browser support">Browser support</h4>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
97.8%
@ -577,9 +623,11 @@ in any specification.</span></p>
<a href="https://caniuse.com/#feat=flexbox" target="_blank">https://caniuse.com/#feat=flexbox</a>
</li>
</ul>
<!-- tags: layout -->
</div>
<div class="snippet">
<h3 id="gradient-text">Gradient text</h3>
<h3 id="gradient-text"><span>Gradient text</span><span class="tags__tag snippet__tag" data-type="visual"><i data-feather="eye"></i>visual</span></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>
@ -589,7 +637,7 @@ in any specification.</span></p>
-webkit-background-clip: text;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<p class="snippet-demo__gradient-text">
Gradient text
@ -605,13 +653,13 @@ in any specification.</span></p>
margin: 0;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>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>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
90.7%
@ -623,9 +671,11 @@ in any specification.</span></p>
<a href="https://caniuse.com/#feat=text-stroke" target="_blank">https://caniuse.com/#feat=text-stroke</a>
</li>
</ul>
<!-- tags: visual -->
</div>
<div class="snippet">
<h3 id="hairline-border">Hairline border</h3>
<h3 id="hairline-border"><span>Hairline border</span><span class="tags__tag snippet__tag" data-type="visual"><i data-feather="eye"></i>visual</span></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>
@ -648,7 +698,7 @@ in any specification.</span></p>
}
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<p class="snippet-demo__hairline-border">Text with a hairline border around it.</p>
</div>
@ -672,12 +722,12 @@ in any specification.</span></p>
}
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>Explanation</h4>
<ol>
<li><code>box-shadow</code>, when only using spread, adds a pseudo-border which can use subpixels*.</li>
<li>Use <code>@media (min-resolution: ...)</code> to check the device pixel ratio (<code>1dppx</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>
<h4>Browser Support</h4>
<div>
<div class="snippet__browser-support">
95.0%
@ -694,9 +744,11 @@ in any specification.</span></p>
</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>
<!-- tags: visual -->
</div>
<div class="snippet">
<h3 id="horizontal-and-vertical-centering">Horizontal and vertical centering</h3>
<h3 id="horizontal-and-vertical-centering"><span>Horizontal and vertical centering</span><span class="tags__tag snippet__tag" data-type="layout"><i data-feather="layout"></i>layout</span></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;
@ -708,7 +760,7 @@ in any specification.</span></p>
align-items: center;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>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>
@ -722,13 +774,13 @@ in any specification.</span></p>
height: 200px;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>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>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
97.8%
@ -740,9 +792,11 @@ in any specification.</span></p>
<a href="https://caniuse.com/#feat=flexbox" target="_blank">https://caniuse.com/#feat=flexbox</a>
</li>
</ul>
<!-- tags: layout -->
</div>
<div class="snippet">
<h3 id="hover-underline-animation">Hover underline animation</h3>
<h3 id="hover-underline-animation"><span>Hover underline animation</span><span class="tags__tag snippet__tag" data-type="animation"><i data-feather="loader"></i>animation</span></h3>
<p>Creates an animated underline effect when the text is hovered over.</p>
<p>
<small><strong>Credit:</strong>
@ -773,7 +827,7 @@ in any specification.</span></p>
transform-origin: bottom left;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<p class="snippet-demo__hover-underline-animation">Hover this text to see the effect!</p>
</div>
@ -800,7 +854,7 @@ in any specification.</span></p>
transform-origin: bottom left;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>Explanation</h4>
<ol>
<li><code>display: inline-block</code> makes the block <code>p</code> an <code>inline-block</code> to prevent the underline from spanning the entire parent width rather than just the content (text).</li>
<li><code>position: relative</code> on the element establishes a Cartesian positioning context for pseudo-elements.</li>
@ -814,7 +868,7 @@ in any specification.</span></p>
<li><code>:hover::after</code> then uses <code>scaleX(1)</code> to transition the width to 100%, then changes the <code>transform-origin</code> to <code>bottom left</code> so that the anchor point is reversed, allowing it transition out in the
other direction when hovered off.</li>
</ol>
<h4 data-type="Browser support">Browser support</h4>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
94.9%
@ -829,9 +883,11 @@ in any specification.</span></p>
<a href="https://caniuse.com/#feat=css-transitions" target="_blank">https://caniuse.com/#feat=css-transitions</a>
</li>
</ul>
<!-- tags: animation -->
</div>
<div class="snippet">
<h3 id="mouse-cursor-gradient-tracking">Mouse cursor gradient tracking</h3>
<h3 id="mouse-cursor-gradient-tracking"><span>Mouse cursor gradient tracking</span><span class="tags__tag snippet__tag" data-type="visual"><i data-feather="eye"></i>visual</span><span class="tags__tag snippet__tag" data-type="interactivity"><i data-feather="edit-2"></i>interactivity</span></h3>
<p>A hover effect where the gradient follows the mouse cursor.</p>
<p>
<small class="snippet__credit"><strong>Credit:</strong>
@ -866,21 +922,21 @@ in any specification.</span></p>
height: var(--size);
background: radial-gradient(circle closest-side, pink, transparent);
transform: translate(-50%, -50%);
transition: width .2s ease, height .2s ease;
transition: width 0.2s ease, height 0.2s ease;
}
.mouse-cursor-gradient-tracking:hover::before {
--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) {
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>
<h4>Demo</h4>
<div class="snippet-demo">
<button class="snippet-demo__mouse-cursor-gradient-tracking">
<span>Hover me</span>
@ -928,14 +984,14 @@ btn.onmousemove = function (e) {
}
})()
</script>
<h4 data-type="Explanation">Explanation</h4>
<h4>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>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
87.2%
@ -950,9 +1006,11 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
<a href="https://caniuse.com/#feat=css-variables" target="_blank">https://caniuse.com/#feat=css-variables</a>
</li>
</ul>
<!-- tags: visual, interactivity -->
</div>
<div class="snippet">
<h3 id="overflow-scroll-gradient">Overflow scroll gradient</h3>
<h3 id="overflow-scroll-gradient"><span>Overflow scroll gradient</span><span class="tags__tag snippet__tag" data-type="visual"><i data-feather="eye"></i>visual</span></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;
@ -969,7 +1027,10 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
bottom: 0;
width: 300px;
height: 25px;
background: linear-gradient(rgba(255, 255, 255, 0.001), white); /* transparent keyword is broken in Safari */
background: linear-gradient(
rgba(255, 255, 255, 0.001),
white
); /* transparent keyword is broken in Safari */
pointer-events: none;
}
.overflow-scroll-gradient__scroller {
@ -982,7 +1043,7 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
text-align: center;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<div class="snippet-demo__overflow-scroll-gradient">
<div class="snippet-demo__overflow-scroll-gradient__scroller">
@ -1016,7 +1077,7 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
<script>
document.querySelector('.snippet-demo__overflow-scroll-gradient__scroller').innerHTML = 'content '.repeat(200)
</script>
<h4 data-type="Explanation">Explanation</h4>
<h4>Explanation</h4>
<ol>
<li><code>position: relative</code> on the parent establishes a Cartesian positioning context for pseudo-elements.</li>
<li><code>::after</code> defines a pseudo element.</li>
@ -1027,7 +1088,7 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
<li><code>bottom: 0</code> positions the pseudo-element at the bottom of the parent.</li>
<li><code>pointer-events: none</code> specifies that the pseudo-element cannot be a target of mouse events, allowing text behind it to still be selectable/interactive.</li>
</ol>
<h4 data-type="Browser support">Browser support</h4>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
94.8%
@ -1039,9 +1100,11 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
<a href="https://caniuse.com/#feat=css-gradients" target="_blank">https://caniuse.com/#feat=css-gradients</a>
</li>
</ul>
<!-- tags: visual -->
</div>
<div class="snippet">
<h3 id="popout-menu">Popout menu</h3>
<h3 id="popout-menu"><span>Popout menu</span><span class="tags__tag snippet__tag" data-type="interactivity"><i data-feather="edit-2"></i>interactivity</span></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;
@ -1061,7 +1124,7 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
visibility: visible;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<div class="snippet-demo__reference">
<div class="snippet-demo__popout-menu">
@ -1092,7 +1155,7 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
visibility: visible;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>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>
@ -1101,16 +1164,18 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
<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>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
99+%
</div>
</div>
<p><span class="snippet__support-note">✅ No caveats.</span></p>
<!-- tags: interactivity -->
</div>
<div class="snippet">
<h3 id="pretty-text-underline">Pretty text underline</h3>
<h3 id="pretty-text-underline"><span>Pretty text underline</span><span class="tags__tag snippet__tag" data-type="visual"><i data-feather="eye"></i>visual</span></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>
@ -1118,10 +1183,7 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
font-family: Arial, sans-serif;
display: inline;
font-size: 18px;
text-shadow: 1px 1px 0 #f5f6f9,
-1px 1px 0 #f5f6f9,
-1px -1px 0 #f5f6f9,
1px -1px 0 #f5f6f9;
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 0.98em;
background-repeat: repeat-x;
@ -1136,7 +1198,7 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
text-shadow: none;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<p class="snippet-demo__pretty-text-underline">Pretty text underline without clipping descending letters.</p>
</div>
@ -1160,7 +1222,7 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
text-shadow: none;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>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>
@ -1169,7 +1231,7 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
<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>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
94.8%
@ -1184,9 +1246,11 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
<a href="https://caniuse.com/#feat=css-gradients" target="_blank">https://caniuse.com/#feat=css-gradients</a>
</li>
</ul>
<!-- tags: visual -->
</div>
<div class="snippet">
<h3 id="shape-separator">Shape separator</h3>
<h3 id="shape-separator"><span>Shape separator</span><span class="tags__tag snippet__tag" data-type="visual"><i data-feather="eye"></i>visual</span></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>
@ -1203,7 +1267,7 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
bottom: 0;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo is-distinct">
<div class="snippet-demo__shape-separator"></div>
</div>
@ -1222,7 +1286,7 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
bottom: 0;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>Explanation</h4>
<ol>
<li><code>position: relative</code> on the element establishes a Cartesian positioning context for pseudo elements.</li>
<li><code>::after</code> defines a pseudo element.</li>
@ -1233,7 +1297,7 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
<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>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
98.0%
@ -1245,17 +1309,20 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
<a href="https://caniuse.com/#feat=svg" target="_blank">https://caniuse.com/#feat=svg</a>
</li>
</ul>
<!-- tags: visual -->
</div>
<div class="snippet">
<h3 id="system-font-stack">System font stack</h3>
<h3 id="system-font-stack"><span>System font stack</span><span class="tags__tag snippet__tag" data-type="visual"><i data-feather="eye"></i>visual</span></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;
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>
<h4>Demo</h4>
<div class="snippet-demo">
<p class="snippet-demo__system-font-stack">This text uses the system font.</p>
</div>
@ -1264,7 +1331,7 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
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>
<h4>Explanation</h4>
<p>The browser looks for each successive font, preferring the first one if possible, and falls back to the next if 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>
@ -1277,16 +1344,18 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
<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>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
99+%
</div>
</div>
<p><span class="snippet__support-note">✅ No caveats.</span></p>
<!-- tags: visual -->
</div>
<div class="snippet">
<h3 id="triangle">Triangle</h3>
<h3 id="triangle"><span>Triangle</span><span class="tags__tag snippet__tag" data-type="visual"><i data-feather="eye"></i>visual</span></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>
@ -1298,7 +1367,7 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
border-right: 20px solid transparent;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<div class="snippet-demo__triangles">
<div class="snippet-demo__triangle snippet-demo__triangle-1"></div>
@ -1345,22 +1414,24 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
border-right: 15px solid transparent;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>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>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
99+%
</div>
</div>
<p><span class="snippet__support-note">✅ No caveats.</span></p>
<!-- tags: visual -->
</div>
<div class="snippet">
<h3 id="truncate-text">Truncate text</h3>
<h3 id="truncate-text"><span>Truncate text</span><span class="tags__tag snippet__tag" data-type="layout"><i data-feather="layout"></i>layout</span></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>
@ -1371,7 +1442,7 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
width: 200px;
}
</code></pre>
<h4 data-type="Demo">Demo</h4>
<h4>Demo</h4>
<div class="snippet-demo">
<p class="snippet-demo__truncate-text">
This text will be truncated if it exceeds 200px in width.
@ -1386,14 +1457,14 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
margin: 0;
}
</style>
<h4 data-type="Explanation">Explanation</h4>
<h4>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>
<li><code>width: 200px;</code> ensures the element has a dimension, to know when to get ellipsis</li>
</ol>
<h4 data-type="Browser support">Browser support</h4>
<h4>Browser support</h4>
<div>
<div class="snippet__browser-support">
98.1%
@ -1405,10 +1476,11 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
<a href="https://caniuse.com/#feat=text-overflow" target="_blank">https://caniuse.com/#feat=text-overflow</a>
</li>
</ul>
<!-- tags: layout -->
</div>
</div>
</main>
</div>
<footer class="footer"></footer>
</body>
</html>

13
package-lock.json generated
View File

@ -1934,6 +1934,11 @@
}
}
},
"classnames": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz",
"integrity": "sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0="
},
"cli-boxes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
@ -3107,6 +3112,14 @@
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
"dev": true
},
"feather-icons": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/feather-icons/-/feather-icons-4.7.0.tgz",
"integrity": "sha512-/dKhEelpOazdN0MtcBZTgqo1tZHn35y40V0sGPi6FzzwNbQ5H6vzTqYWWMeRtKw5JePmog7RthR/PYPA1lYNFA==",
"requires": {
"classnames": "2.2.5"
}
},
"filename-regex": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",

View File

@ -25,5 +25,8 @@
"prettier": "^1.10.2",
"pretty": "^2.0.0",
"prismjs": "^1.11.0"
},
"dependencies": {
"feather-icons": "^4.7.0"
}
}

View File

@ -2,71 +2,134 @@ 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 { toKebabCase, createElement, template, dom } = require('../utils/utils.js')
const SNIPPETS_PATH = './snippets'
const SNIPPET_CONTAINER_SELECTOR = '.main > .container'
const TAGS = [
{
name: 'all',
icon: 'check'
},
{
name: 'layout',
icon: 'layout'
},
{
name: 'visual',
icon: 'eye'
},
{
name: 'animation',
icon: 'loader'
},
{
name: 'interactivity',
icon: 'edit-2'
}
]
const createElement = str => {
const el = document.createElement('div')
el.innerHTML = str
return el.firstElementChild
const renderer = new marked.Renderer()
renderer.heading = (text, level) => {
if (level === 3) {
return `<h${level} id="${toKebabCase(text)}"><span>${text}</span></h${level}>`
} else {
return ['HTML', 'CSS', 'JavaScript'].includes(text)
? `<h${level} data-type="${text}">${text}</h${level}>`
: `<h${level}>${text}</h${level}>`
}
}
renderer.link = (url, _, text) => `<a href="${url}" target="_blank">${text || url}</a>`
const document = dom('./src/html/index.html')
const components = {
backToTopButton: dom('./src/html/components/back-to-top-button.html'),
sidebar: dom('./src/html/components/sidebar.html'),
header: dom('./src/html/components/header.html'),
main: dom('./src/html/components/main.html'),
tags: dom('./src/html/components/tags.html')
}
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)
const snippetContainer = components.main.querySelector('.container')
const sidebarLinkContainer = components.sidebar.querySelector('.sidebar__links')
TAGS.slice(1).forEach(tag => {
sidebarLinkContainer.append(
createElement(`
<section data-type="${tag.name}" class="sidebar__section">
<h4 class="sidebar__section-heading">${tag.name}</h4>
</section>
`)
)
})
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 snippetEl = createElement(`<div class="snippet">${markdown}</div>`)
snippetContainer.append(snippetEl)
// browser support usage
const featUsageShares = (snippetData.match(/https?:\/\/caniuse\.com\/#feat=.*/g) || []).map(
feat => {
const featData = caniuseDb.data[feat.match(/#feat=(.*)/)[1]]
return featData ? Number(featData.usage_perc_y + featData.usage_perc_a) : 100
}
)
el.querySelector('h4:last-of-type').after(
const browserSupportHeading = snippetEl.querySelector('h4:last-of-type')
browserSupportHeading.after(
createElement(`
<div>
<div class="snippet__browser-support">
${featUsageShares.length ? Math.min(...featUsageShares).toPrecision(3) : '99+'}%
</div>
</div>
`)
`)
)
// sidebar link
const link = createElement(
`<a class="sidebar__link" href="#${snippetFile.replace('.md', '')}">${
snippetEl.querySelector('h3').innerHTML
}</a>`
)
// tags
const tags = (snippetData.match(/<!--\s*tags:\s*(.+?)-->/) || [, ''])[1]
.split(/,\s*/)
.forEach(tag => {
tag = tag.trim().toLowerCase()
snippetEl
.querySelector('h3')
.append(
createElement(
`<span class="tags__tag snippet__tag" data-type="${tag}"><i data-feather="${
TAGS.find(t => t.name === tag).icon
}"></i>${tag}</span>`
)
)
sidebarLinkContainer.querySelector(`section[data-type="${tag}"]`).append(link)
})
}
// build dom
TAGS.forEach(tag =>
components.tags.append(
createElement(
`<button class="tags__tag is-large ${tag.name === 'all' ? 'is-active' : ''}" data-type="${
tag.name
}"><i data-feather="${tag.icon}"></i>${tag.name}</button>`
)
)
)
const content = document.querySelector('.content-wrapper')
content.before(components.backToTopButton)
content.before(components.sidebar)
content.append(components.header)
content.append(components.main)
components.main.querySelector('.container').prepend(components.tags)
// doctype declaration gets stripped, add it back in
const html = `<!DOCTYPE html>
${pretty(document.documentElement.outerHTML, { ocd: true })}

View File

@ -44,3 +44,5 @@ html {
<span class="snippet__support-note">✅ No caveats.</span>
* https://caniuse.com/#feat=css3-boxsizing
<!-- tags: layout -->

View File

@ -18,7 +18,7 @@ Ensures that an element self-clears its children.
```css
.clearfix::after {
content: "";
content: '';
display: block;
clear: both;
}
@ -60,3 +60,5 @@ Ensures that an element self-clears its children.
#### Browser support
<span class="snippet__support-note">✅ No caveats.</span>
<!-- tags: layout -->

View File

@ -43,3 +43,5 @@ causes an element's height to become a percentage of its parent's width, i.e. `5
#### Browser support
<span class="snippet__support-note">⚠️ `padding-top` pushes any content within the element to the bottom.</span>
<!-- tags: layout -->

View File

@ -19,13 +19,13 @@ Customizes the scrollbar style for the document and elements with scrollable ove
}
::-webkit-scrollbar-track {
box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
box-shadow: inset 0 0 6px rgba(0,0,0,0.5);
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.5);
}
/* Scrollable element */
@ -80,3 +80,5 @@ There are many other pseudo-elements that you can use to style scrollbars. For m
<span class="snippet__support-note">⚠️ Scrollbar styling doesn't appear to be on any standards track.</span>
* https://caniuse.com/#feat=css-scrollbar
<!-- tags: visual -->

View File

@ -48,3 +48,5 @@ Changes the styling of text selection.
in any specification.</span>
* https://caniuse.com/#feat=css-selection
<!-- tags: visual -->

View File

@ -39,3 +39,5 @@ Makes the content unselectable.
<span class="snippet__support-note">⚠️ Requires prefixes for full support.</span>
* https://caniuse.com/#feat=user-select-none
<!-- tags: interactivity -->

View File

@ -12,8 +12,12 @@ Creates a donut spinner that can be used to indicate the loading of content.
```css
@keyframes donut-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.donut {
display: inline-block;
@ -59,3 +63,5 @@ serve as the loading indicator for the donut. Use `animation` to rotate the elem
* https://caniuse.com/#feat=css-animation
* https://caniuse.com/#feat=transforms2d
<!-- tags: animation -->

View File

@ -104,3 +104,5 @@ The variables are defined globally within the `:root` CSS pseudo-class which mat
<span class="snippet__support-note">✅ No caveats.</span>
* https://caniuse.com/#feat=css-variables
<!-- tags: animation -->

View File

@ -49,3 +49,5 @@ of the background.
<span class="snippet__support-note">✅ No caveats.</span>
* https://caniuse.com/#feat=css-textshadow
<!-- tags: visual -->

View File

@ -51,3 +51,5 @@ Alternatively, use `justify-content: space-around` to distribute the children wi
<span class="snippet__support-note">⚠️ Needs prefixes for full support.</span>
* https://caniuse.com/#feat=flexbox
<!-- tags: layout -->

View File

@ -49,3 +49,5 @@ Gives text a gradient color.
<span class="snippet__support-note">⚠️ Uses non-standard properties.</span>
* https://caniuse.com/#feat=text-stroke
<!-- tags: visual -->

View File

@ -81,3 +81,5 @@ very sharp and crisp.
<hr>
\*Chrome does not support subpixel values on `border`. Safari does not support subpixel values on `box-shadow`. Firefox supports subpixel values on both.
<!-- tags: visual -->

View File

@ -48,3 +48,5 @@ Horizontally and vertically centers a child element within a parent element.
<span class="snippet__support-note">⚠️ Needs prefixes for full support.</span>
* https://caniuse.com/#feat=flexbox
<!-- tags: layout -->

View File

@ -69,7 +69,7 @@ Creates an animated underline effect when the text is hovered over.
#### Explanation
1. `display: inline-block` makes the block `p` an `inline-block` to prevent the underline from
spanning the entire parent width rather than just the content (text).
spanning the entire parent width rather than just the content (text).
2. `position: relative` on the element establishes a Cartesian positioning context for pseudo-elements.
3. `::after` defines a pseudo-element.
4. `position: absolute` takes the pseudo element out of the flow of the document and positions it in relation to the parent.
@ -77,11 +77,11 @@ spanning the entire parent width rather than just the content (text).
6. `transform: scaleX(0)` initially scales the pseudo element to 0 so it has no width and is not visible.
7. `bottom: 0` and `left: 0` position it to the bottom left of the block.
8. `transition: transform 0.25s ease-out` means changes to `transform` will be transitioned over 0.25 seconds
with an `ease-out` timing function.
with an `ease-out` timing function.
9. `transform-origin: bottom right` means the transform anchor point is positioned at the bottom right of the block.
10. `:hover::after` then uses `scaleX(1)` to transition the width to 100%, then changes the `transform-origin`
to `bottom left` so that the anchor point is reversed, allowing it transition out in the other direction when
hovered off.
to `bottom left` so that the anchor point is reversed, allowing it transition out in the other direction when
hovered off.
#### Browser support
@ -89,3 +89,5 @@ hovered off.
* https://caniuse.com/#feat=transforms2d
* https://caniuse.com/#feat=css-transitions
<!-- tags: animation -->

View File

@ -41,7 +41,7 @@ A hover effect where the gradient follows the mouse cursor.
height: var(--size);
background: radial-gradient(circle closest-side, pink, transparent);
transform: translate(-50%, -50%);
transition: width .2s ease, height .2s ease;
transition: width 0.2s ease, height 0.2s ease;
}
.mouse-cursor-gradient-tracking:hover::before {
@ -53,7 +53,7 @@ A hover effect where the gradient follows the mouse cursor.
```js
var btn = document.querySelector('.mouse-cursor-gradient-tracking')
btn.onmousemove = function (e) {
btn.onmousemove = function(e) {
var x = e.pageX - btn.offsetLeft
var y = e.pageY - btn.offsetTop
btn.style.setProperty('--x', x + 'px')
@ -131,7 +131,10 @@ 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
<!-- tags: visual, interactivity -->

View File

@ -24,7 +24,10 @@ Adds a fading gradient to an overflowing element to better indicate there is mor
bottom: 0;
width: 300px;
height: 25px;
background: linear-gradient(rgba(255, 255, 255, 0.001), white); /* transparent keyword is broken in Safari */
background: linear-gradient(
rgba(255, 255, 255, 0.001),
white
); /* transparent keyword is broken in Safari */
pointer-events: none;
}
.overflow-scroll-gradient__scroller {
@ -81,10 +84,10 @@ document.querySelector('.snippet-demo__overflow-scroll-gradient__scroller').inne
1. `position: relative` on the parent establishes a Cartesian positioning context for pseudo-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).
(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).
the pseudo element).
6. `height: 25px` is the height of the fading gradient pseudo-element, which should be kept relatively small.
7. `bottom: 0` positions the pseudo-element at the bottom of the parent.
8. `pointer-events: none` specifies that the pseudo-element cannot be a target of mouse events, allowing text behind it to still be selectable/interactive.
@ -94,3 +97,5 @@ document.querySelector('.snippet-demo__overflow-scroll-gradient__scroller').inne
<span class="snippet__support-note">✅ No caveats.</span>
* https://caniuse.com/#feat=css-gradients
<!-- tags: visual -->

View File

@ -75,3 +75,5 @@ Reveals an interactive popout menu on hover.
#### Browser support
<span class="snippet__support-note">✅ No caveats.</span>
<!-- tags: interactivity -->

View File

@ -16,10 +16,7 @@ Natively implemented as `text-decoration-skip-ink: auto` but it has less control
font-family: Arial, sans-serif;
display: inline;
font-size: 18px;
text-shadow: 1px 1px 0 #f5f6f9,
-1px 1px 0 #f5f6f9,
-1px -1px 0 #f5f6f9,
1px -1px 0 #f5f6f9;
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 0.98em;
background-repeat: repeat-x;
@ -70,13 +67,13 @@ Natively implemented as `text-decoration-skip-ink: auto` but it has less control
#### 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.
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`).
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.
selection.
#### Browser support
@ -84,3 +81,5 @@ Natively implemented as `text-decoration-skip-ink: auto` but it has less control
* https://caniuse.com/#feat=css-textshadow
* https://caniuse.com/#feat=css-gradients
<!-- tags: visual -->

View File

@ -64,3 +64,5 @@ Uses an SVG shape to separate two different blocks to create more a interesting
<span class="snippet__support-note">✅ No caveats.</span>
* https://caniuse.com/#feat=svg
<!-- tags: visual -->

View File

@ -12,7 +12,8 @@ Uses the native font of the operating system to get close to a native app feel.
```css
.system-font-stack {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu,
Cantarell, 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
```
@ -46,3 +47,5 @@ falls back to the next if it cannot find the font (on the system or defined in C
#### Browser support
<span class="snippet__support-note">✅ No caveats.</span>
<!-- tags: visual -->

View File

@ -89,3 +89,5 @@ Experiment with the `px` values to change the proportion of the triangle.
#### Browser support
<span class="snippet__support-note">✅ No caveats.</span>
<!-- tags: visual -->

View File

@ -51,3 +51,5 @@ If the text is longer than one line, it will be truncated and end with an ellips
<span class="snippet__support-note">⚠️ Only works for single line elements.</span>
* https://caniuse.com/#feat=text-overflow
<!-- tags: layout -->

View File

@ -35,6 +35,7 @@
max-height: 378px;
margin-top: 44px;
box-shadow: 0 0.25rem 0.5rem -0.1rem rgba(0, 32, 128, 0.2);
padding-bottom: 1rem;
&.is-active {
transform: rotateX(0);
@ -59,6 +60,16 @@
border-color: pink;
}
}
&__section {
padding: 0 0.75rem;
}
&__section-heading {
text-transform: capitalize;
color: #e3f5ff;
margin-bottom: 0.5rem;
}
}
@media (min-width: 992px) {

View File

@ -14,6 +14,10 @@
margin-bottom: 1.25rem;
margin-top: 0;
line-height: 1.3;
span:not(.snippet__tag) {
margin-right: 0.75rem;
}
}
code:not([class*='lang']) {
@ -42,16 +46,20 @@
h4 {
display: inline-block;
margin: 1rem 0 0.5rem;
font-size: 1.1rem;
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] {
background: #333;
padding: 0 0.5rem;
border-radius: 3px;
font-size: 0.9rem;
text-transform: uppercase;
border: 1px solid #c6d6ea;
border-bottom-color: darken(#c6d6ea, 5);
background: white;
box-shadow: 0 0.25rem 0.5rem -0.1rem rgba(0, 32, 64, 0.15);
}
&[data-type='HTML'] {
color: white;
@ -107,6 +115,9 @@
top: 1rem;
right: 0;
}
&__tag {
}
}
.snippet-demo {

View File

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

View File

@ -4,3 +4,4 @@
@import './components/header.scss';
@import './components/snippet.scss';
@import './components/back-to-top-button.scss';
@import './components/tags.scss';

View File

@ -0,0 +1 @@
<button class="back-to-top-button" aria-label="back to top"></button>

View File

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

View File

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

View File

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

View File

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

View File

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

15
src/html/index.html Normal file
View File

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

View File

@ -1,27 +1,23 @@
import jump from '../deps/jump'
import { select, selectAll, easeOutQuint } from '../deps/utils'
const menu = select('.hamburger')
const links = select('.sidebar__links')
const sections = selectAll('.sidebar__section')
const ACTIVE_CLASS = 'is-active'
const toggle = () => {
const els = [menu, links]
els.forEach(el => el.classList.toggle(ACTIVE_CLASS))
menu.setAttribute('aria-expanded', menu.classList.contains(ACTIVE_CLASS) ? 'true' : 'false')
if (window.innerWidth <= 991) {
const els = [menu, links]
els.forEach(el => el.classList.toggle(ACTIVE_CLASS))
menu.setAttribute('aria-expanded', menu.classList.contains(ACTIVE_CLASS) ? 'true' : 'false')
}
}
menu.addEventListener('click', toggle)
links.addEventListener('click', e => {
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
})
setTimeout(toggle, 100)
}
})
@ -35,4 +31,13 @@ document.addEventListener('click', e => {
}
})
EventHub.on('Tag.click', data => {
sections.forEach(section => {
section.style.display = 'block'
if (section.dataset.type !== data.type && data.type !== 'all') {
section.style.display = 'none'
}
})
})
export default { toggle }

View File

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

14
src/js/components/Tag.js Normal file
View File

@ -0,0 +1,14 @@
import { select, selectAll, on } from '../deps/utils'
const tagButtons = selectAll('button.tags__tag')
const onClick = function() {
tagButtons.forEach(button => button.classList.remove('is-active'))
this.classList.add('is-active')
EventHub.emit('Tag.click', {
type: this.dataset.type
})
}
tagButtons.forEach(button => on(button, 'click', onClick))

View File

@ -1,196 +0,0 @@
;(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)
}
})()
})

View File

@ -1,8 +1,34 @@
export const select = s => document.querySelector(s)
export const selectAll = s => [].slice.call(document.querySelectorAll(s))
export const select = (s, parent = document) => parent.querySelector(s)
export const selectAll = (s, parent = document) => [].slice.call(parent.querySelectorAll(s))
export const scrollY = () => window.scrollY || window.pageYOffset
export const easeOutQuint = (t, b, c, d) => c * ((t = t / d - 1) * t ** 4 + 1) + b
export const on = (el, evt, fn, opts = {}) => {
const delegatorFn = e => e.target.matches(opts.target) && fn.call(e.target, e)
el.addEventListener(evt, opts.target ? delegatorFn : fn, opts.options || false)
if (opts.target) return delegatorFn
}
export const createEventHub = () => ({
hub: Object.create(null),
emit(event, data) {
;(this.hub[event] || []).forEach(handler => handler(data))
},
on(event, handler) {
if (!this.hub[event]) this.hub[event] = []
this.hub[event].push(handler)
},
off(event, handler) {
const i = (this.hub[event] || []).findIndex(h => h === handler)
if (i > -1) this.hub[event].splice(i, 1)
}
})
window.EventHub = createEventHub()
/*
* Make iOS behave normally.
*/

View File

@ -1,6 +1,8 @@
// Deps
import 'normalize.css'
import 'prismjs'
import feather from 'feather-icons'
feather.replace()
// CSS
import '../css/deps/prism.css'
@ -10,5 +12,7 @@ import '../css/index.scss'
import './deps/polyfills'
// Components
import Menu from './components/Menu'
import Sidebar from './components/Sidebar'
import BackToTopButton from './components/BackToTopButton'
import Tag from './components/Tag'
import Snippet from './components/Snippet'

View File

@ -1,8 +1,20 @@
const fs = require('fs')
const { JSDOM } = require('jsdom')
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('-');
.join('-')
exports.emptyHTML = (...els) => els.forEach(el => (el.innerHTML = ''))
exports.dom = path => {
const doc = new JSDOM(fs.readFileSync(path, 'utf8')).window.document
return path.includes('component') ? doc.body.firstElementChild : doc
}
exports.createElement = str => {
const el = new JSDOM().window.document.createElement('div')
el.innerHTML = str
return el.firstElementChild
}