blog/src/lib/Sidenote.svelte

173 lines
4.5 KiB
Svelte

<style>
/* always applicable */
:global(body) {
counter-reset: sidenote;
}
.counter {
counter-increment: sidenote;
color: #444;
margin-left: 0.05rem;
}
.counter:after {
font-size: 0.75em;
position: relative;
bottom: 0.3rem;
color: #8c0606;
}
.sidenote {
color: #555;
font-size: 0.8rem;
}
.sidenote:before {
content: counter(sidenote) " ";
/* absolute positioning puts it at the top-left corner of the sidenote, overlapping with the content
(because the sidenote is floated it counts as a positioned parent, I think) */
position: absolute;
/* translate moves it out to the left (and just a touch up to mimic the superscript efect)
-100% refers to the width of the element, so it pushes it out further if necessary (i.e. two digits instead of one) */
transform: translate(calc(-100% - 0.2rem), -0.15rem);
font-size: 0.75rem;
color: #8c0606;
}
.sidenote-toggle {
display: none;
}
/* desktop display */
@media(min-width: 70em) {
.counter:after {
content: counter(sidenote);
}
.sidenote {
--gap: 2rem;
--sidenote-width: min(14rem, calc(50vw - var(--gap) - var(--content-width) / 2));
width: var(--sidenote-width);
hyphens: auto;
position: relative;
float: right;
clear: right;
margin-right: calc(0rem - var(--sidenote-width) - var(--gap)); /* gives us 2rem of space between content and sidenote */
margin-bottom: 0.7rem;
}
/* fade-in animation */
.sidenote {
opacity: 0;
animation: fade-in 600ms ease-out;
animation-delay: 500ms;
animation-fill-mode: forwards;
}
@keyframes fade-in {
from {opacity: 0;}
to {opacity: 1;}
}
.nested.sidenote {
margin-right: 0;
margin-top: 0.7rem;
margin-bottom: 0;
}
.dismiss {
display: none;
}
}
/* mobile display */
@media (max-width: 70em) {
.counter:after {
content: "[" counter(sidenote) "]";
}
.counter:hover:after {
color: #000;
cursor: pointer;
}
.sidenote {
box-sizing: border-box;
position: fixed;
z-index: 1;
left: 0;
bottom: 0;
width: 100vw;
padding-top: 1rem;
padding-bottom: 1rem;
--pad: max(1rem, calc(50vw - var(--content-width) / 2));
padding-left: var(--pad);
padding-right: var(--pad);
background-color: #fff;
box-shadow: 0 -2px 4px -1px rgba(0, 0, 0, 0.06), 0 -2px 12px -2px rgba(0, 0, 0, 0.1);
display: none;
}
.sidenote-toggle:checked + .sidenote {
display: block;
}
.dismiss {
position: absolute;
right: 1.5rem;
top: -0.2rem;
font-size: 1.25rem;
color: #8c0606;
cursor: pointer;
}
.dismiss:hover {
transform: scale(1.1);
font-weight: 800;
}
}
</style>
<script context="module">
var activeToggle = null;
</script>
<script>
import { onMount } from 'svelte';
let noteBody;
let nested = false;
onMount(() => {
// check to see if the parent node is also a sidenote, if so move this one to the end
let parentNote = noteBody.parentElement.closest('span.sidenote');
if (parentNote) {
noteBody.remove();
parentNote.appendChild(noteBody);
nested = true;
}
});
const id = Math.random().toString().slice(2);
let toggle;
function toggleState() {
if (activeToggle === toggle) {
activeToggle = null;
}
else if (activeToggle !== null) {
activeToggle.checked = false;
activeToggle = toggle;
}
else {
activeToggle = toggle;
}
}
</script>
<label for={id} on:click={toggleState} class="counter"></label>
<input {id} bind:this={toggle} type="checkbox" class="sidenote-toggle" />
<span class="sidenote" class:nested bind:this={noteBody}>
<label class="dismiss" for={id} on:click={toggleState}>&times;</label>
<slot></slot>
</span>