animated logo - nberlette.svg

Animated personal logomark with the Ukrainian flag colors. SVG and CSS.

by Nicholas Berlette

HTML

<script src="https://unpkg.com/windicss-runtime-dom"></script>
<!-- start emblem -->
<div class="bg-inherit min-w-screen min-h-screen w-full h-screen m-0 p-12 relative" style="box-shadow:0 -3rem 15rem -2rem inset #0009;">
  <div class="flex flex-col place-items-center justify-around items-center align-center text-center absolute top-1/2 -translate-y-1/2 left-1/2 transform -translate-x-1/2">
	<div class="w-auto h-[60vh] <md:w-[80vw] max-w-[80vw] max-h-[50vh]">
      <div class="hover:animate__jello animate__animated transform hover:animate__infinite">
      <svg viewBox="-20 -20 140 140" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" class="fill-none stroke-current stroke-0 text-inherit group transform animate__flipInY animate__animated animate__repeat-1 hover:animate__wobble hover:animate__infinite hover:animate__delay-0s" style="animation-delay:1s;" id="nb-emblem">
        <g id="nb">
          <circle cx="50" cy="50" r="50" class="stroke-current fill-transparent outer-circle" id="c-o" />
          <circle cx="50" cy="50" r="40" class="stroke-current fill-transparent inner-circle" id="c-i" />
          <path d="M34.5,31 l0,38 l8,0 l0,-24 l15,24 l8,0 l0,-38 l-8,0 l0,24 l-15,-24 Z" fill="transparent" class="letter-n text-inherit group-hover:animate-pulse" id="n-b" />
        </g>
				<use href="#b" />
				<g id="ukraine-overlay">
					<path fill="#044bbb" d="M-571-473H662v822H-571z" clip-path="url(#a)" />
					<path fill="#ffc500" d="M-571 50H662v411H-571z" clip-path="url(#a)" />	
					<clipPath id="a"><use href="#n-b" overflow="visible"/></clipPath>
				</g>
      </svg>
      </div>
		</div>
    <!-- end emblem -->
			<header>
			<h1 class="animate__animate animate__backInDown animate__delay-3s animate__repeat-1 transform text-6xl font-[Metropolis] font-thin lowercase tracking-tighter">@nberlette</h1>
			<h2 class="animate__animate animate__backInUp transform text-3xl font-[Metropolis] font-thin lowercase tracking-tight">Fullstack...

CSS

/* @import "https://unpkg.com/[email protected]/animate.css"; */
@import "https://unpkg.com/@typehaus/[email protected]/700.css";
@import "https://unpkg.com/@typehaus/[email protected]/100.css";

/* all emblems */
[id^="nb-emblem"] {
  cursor: wait;
}

[id^="nb-emblem"] .letter-n {
  stroke-width: 1;
  transform-origin: 35.75% 35.75%;
}

[id^="nb-emblem"]:hover .letter-n {
  fill: black;
  animation-play-state: running;
}

[id^="nb-emblem"] .inner-circle {
  stroke-width: 4;
  transform-origin: 35.75% 35.75%;
}

[id^="nb-emblem"] .outer-circle {
  stroke-width: 2;
  transform-origin: 35.75% 35.75%;
}

:is(h1, h2, h3) {
	font-family: 'Metropolis', 'Inter', sans-serif !important;
	cursor: default;
	margin:0;
	padding:0;
}

h1 {
	margin-bottom: 1rem;
	padding: 1rem 1rem 1rem 0.5rem;
	border-bottom: 1px dashed #fcc50044;
}

h1 + h2 {
	margin-top: 0.666em;
}

#nb-emblem {
	/* transition: transform 0.5s 1s ease-in, opacity 0.666s 1.1s ease-in; */
	/* transform-origin: 50% 100%; */
	/* transform: perspective(500px) scale3d(var(--tw-scale-x, 0), var(--tw-scale-y, 0), var(--tw-scale-z, 1)); */
	/* opacity: 0; */
	filter: drop-shadow(0 0.1rem 0.2rem #0003);
}

#ukraine-overlay {
	transition: 0.5s fill 1.5s ease-in;
}

/* 	#ukraine-overlay path {
		fill: #044bbb;
		transition: inherit !important;	
	} */
	#ukraine-overlay path:last-child {
		fill: #ffc500;
		transition: 0.5s fill 2s ease-in, 0.6 transform 3s ease-in !important;
	}
	
/* emblem #1 */
#nb-emblem .letter-n {
	fill: #044bbb;
  transition: fill 2.5s ease-out, stroke 1s 3s;
	stroke: #ffc50066;
}

#nb-emblem:hover .letter-n {
  animation-play-state: running;
	stroke: #ffc500cc;
}

#nb-emblem .inner-circle {
  transition: stroke-width 6s, transform 2s, stroke 1.33s 2.5s ease-in;
}

#nb-emblem:hover .inner-circle {
	stroke: #003aaa;
  stroke-width: 10 !important;
  transform: scale(1.1);
}

#nb-emblem .outer-circle {
  stroke-dasharray: 20 4;
  stroke-dashoffset: 20;
	stroke-opacity: 0.5;
  transition:...

JavaScript

const resources = [
	{ type: 'module', prefetch: true, url: 'https://unpkg.com/windicss-runtime-dom' },
	{ type: 'stylesheet', prefetch: true, url: 'https://unpkg.com/@typehaus/metropolis@latest/100.css' },
	{ type: 'stylesheet', prefetch: true, url: 'https://unpkg.com/@typehaus/metropolis@latest/300.css' },
	{ type: 'stylesheet', prefetch: true, url: 'https://unpkg.com/@typehaus/metropolis@latest/500.css' },
	{ type: 'stylesheet', prefetch: true, url: 'https://cdn.jsdelivr.net/npm/@typehaus/metropolis@latest/700.css' },
	{ type: 'icon', prefetch: true, url: 'https://cdn.berlette.com/brand/logo.svg' }
];

let hosts = [...new Set(resources.map(r => r.url.startsWith('http') && (new URL(r.url).origin)).filter(Boolean))]

const preconnects = hosts.flatMap(href => 
['preconnect', 'prefetch-dns'].flatMap(rel => 
	`<link rel="${rel}" href="${href}" />`
))

const [prefetches, stylesheets] = resources
.filter(r => r.type.startsWith('style'))
.reduce((a, c) => [
	[...a[0], ...(c.prefetch ? [`<link rel="prefetch" as="style" type="text/css;charset=utf-8" href="${c.url}" ${c.url.startsWith('http') ? 'crossorigin="anonymous" ' : ''}/>`] : [])],
	[...a[1], `<link rel="stylesheet" type="text/css;charset=utf-8" href="${c.url}" ${c.url.startsWith('http') ? 'crossorigin="anonymous" ' : ''}/>`],
], [[], []])

console.log(hosts)
console.log(preconnects)
console.log(prefetches)
console.log(stylesheets)