Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 64 additions & 0 deletions public/signature-animate.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
130 changes: 130 additions & 0 deletions src/components/Signature.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---
interface Props {
class?: string
}

const { class: className = '' } = Astro.props
---

<div class={`signature-root ${className}`} data-signature-root>
<svg class="signature-svg" viewBox="0 0 100 100" aria-hidden="true">
<path
class="signature-path"
pathLength="1"
transform="translate(-8 -7)"
d="M18 30
C18 39,18 47,20 50
C23 55,28 54,31 49
C35 43,40 37,45 29
C49 23,52 17,53 13
C52 18,51 24,50 31
C49 42,49 53,49 64
C49 78,49 90,47 98
C44 101,40 100,37 95
C33 88,31 80,31 72
C31 63,35 56,41 50
C48 43,56 38,65 36
C72 35,76 35,78 38
C79 42,78 48,78 53
C79 58,82 59,85 54
C89 48,90 43,90 39
C89 44,89 49,90 53
C91 58,95 58,98 55"
/>
</svg>
</div>

<script>
const DEFAULT_DURATION_MS = 2400
const EASING = 'cubic-bezier(0.4, 0, 0.2, 1)'

function setupSignature(root: Element): void {
const path = root.querySelector<SVGPathElement>('.signature-path')
if (!path) {
return
}
const signaturePath = path

let token = 0
const cssDuration = Number.parseFloat(getComputedStyle(root).getPropertyValue('--signature-duration'))
const durationMs = Number.isFinite(cssDuration) && cssDuration > 0
? cssDuration * 1000
: DEFAULT_DURATION_MS

async function play(from: number, to: number, duration: number): Promise<void> {
const animation = signaturePath.animate(
[{ strokeDashoffset: String(from) }, { strokeDashoffset: String(to) }],
{
duration: Math.max(1, duration),
easing: EASING,
fill: 'forwards',
},
)

try {
await animation.finished
signaturePath.style.strokeDashoffset = String(to)
}
catch {
// Animation was cancelled (e.g. mouseenter), ignore
}
}

function getOffset(): number {
const offset = Number.parseFloat(getComputedStyle(signaturePath).strokeDashoffset)
if (Number.isNaN(offset)) {
return 0
}

return Math.max(0, Math.min(1, offset))
}

void play(1, 0, durationMs)

root.addEventListener('mouseenter', () => {
token += 1
const run = token
const offset = getOffset()
signaturePath.getAnimations().forEach(animation => animation.cancel())

const reverseDuration = Math.round(durationMs * (1 - offset))

void play(offset, 1, reverseDuration).then(() => {
if (run !== token) {
return
}

void play(1, 0, durationMs)
})
})
}

document.querySelectorAll('[data-signature-root]').forEach((root) => {
setupSignature(root)
})
</script>

<style>
.signature-root {
--signature-stroke: #0f172a;
--signature-duration: 2.4s;
display: inline-block;
}

.signature-svg {
width: 100%;
height: 100%;
overflow: visible;
}

.signature-path {
fill: none;
stroke: var(--signature-stroke);
stroke-width: 2.3;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 1;
stroke-dashoffset: 1;
}
</style>

3 changes: 2 additions & 1 deletion src/components/Thought.astro
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface Props {
const { thought } = Astro.props
const { Content } = await render(thought)

const TAG_LEADING_HASH = /^#/
const tags = thought.data.tags ?? []
---

Expand All @@ -29,7 +30,7 @@ const tags = thought.data.tags ?? []
? (
tags.map((tag: string) => (
<span class="rounded border border-gray-200/90 bg-gray-50/90 px-1.5 py-0.5 text-xs text-gray-700">
#{tag.replace(/^#/, '')}
#{tag.replace(TAG_LEADING_HASH, '')}
</span>
))
)
Expand Down
5 changes: 4 additions & 1 deletion src/components/cards/Profile.astro
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
---
import Avatar from '../Avatar.astro'
import CornerMarkers from '../CornerMarkers.astro'
import Signature from '../Signature.astro'
---

<div class="relative bg-white border border-gray-200 p-6 transition-all duration-300 hover:border-gray-400">
<CornerMarkers />

<div class="flex items-center gap-4">
<div class="h-16 w-16 flex-shrink-0 transition-transform duration-300">
<div class="h-16 w-16 shrink-0 transition-transform duration-300">
<Avatar />
</div>
<div class="flex-1 flex flex-col justify-between">
<h1 class="text-2xl font-bold text-gray-900 font-mono">yuler</h1>
<div class="mt-1 text-sm font-medium text-gray-500">A programmer & father of two</div>
</div>
</div>

<Signature class="absolute bottom-4 right-4 h-20 w-20 z-10! transition-transform duration-300 " />
</div>
4 changes: 2 additions & 2 deletions src/pages/workouts/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ const showEmptyRangeState = Boolean(serverFilterRange && visibleInServerRange ==

<!-- Prerender has no search string; sync filter from the real URL before paint settles. -->
<script is:inline>
(function () {
const YMD = /^\d{4}-\d{2}-\d{2}$/
const YMD = /^\d{4}-\d{2}-\d{2}$/
;(function () {
const list = document.getElementById('activity-list')
const countEl = document.getElementById('activity-count')
const emptyEl = document.getElementById('empty-state')
Expand Down