mirror of
https://github.com/BradNut/personal-website-sveltekit
synced 2025-09-08 23:20:18 +00:00
Updating external link and about page, subsequent components, to use it.
This commit is contained in:
parent
15135369f4
commit
553ce0f783
12 changed files with 966 additions and 1021 deletions
22
package.json
22
package.json
|
|
@ -18,15 +18,15 @@
|
|||
"packageManager": "pnpm@9.11.0",
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.9.4",
|
||||
"@playwright/test": "^1.49.0",
|
||||
"@sveltejs/adapter-node": "^5.2.9",
|
||||
"@sveltejs/enhanced-img": "^0.4.1",
|
||||
"@sveltejs/kit": "^2.9.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.1",
|
||||
"@playwright/test": "^1.49.1",
|
||||
"@sveltejs/adapter-node": "^5.2.10",
|
||||
"@sveltejs/enhanced-img": "^0.4.3",
|
||||
"@sveltejs/kit": "^2.11.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.2",
|
||||
"@zerodevx/svelte-img": "^2.1.2",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"just-intersect": "^4.3.0",
|
||||
"mdsvex": "^0.11.2",
|
||||
"mdsvex": "^0.12.3",
|
||||
"mdsvex-relative-images": "^1.0.3",
|
||||
"postcss": "^8.4.49",
|
||||
"postcss-import": "^16.1.0",
|
||||
|
|
@ -36,7 +36,7 @@
|
|||
"satori-html": "^0.3.2",
|
||||
"scrape-it": "^6.1.3",
|
||||
"sharp": "^0.33.5",
|
||||
"svelte": "^5.9.0",
|
||||
"svelte": "^5.11.3",
|
||||
"svelte-check": "^4.1.0",
|
||||
"svelte-meta-tags": "^4.0.4",
|
||||
"svelte-preprocess": "^6.0.3",
|
||||
|
|
@ -46,19 +46,19 @@
|
|||
"vanilla-lazyload": "^19.1.3",
|
||||
"vite": "^6.0.2",
|
||||
"vite-imagetools": "^7.0.5",
|
||||
"vitest": "^1.6.0"
|
||||
"vitest": "^2.1.8"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@resvg/resvg-js": "^2.6.2",
|
||||
"@sveltejs/adapter-vercel": "^5.5.0",
|
||||
"@sveltejs/adapter-vercel": "^5.5.2",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@unpic/svelte": "^0.0.56",
|
||||
"@unpic/svelte": "^0.0.57",
|
||||
"@vercel/og": "^0.6.4",
|
||||
"bits-ui": "1.0.0-next.66",
|
||||
"flexsearch": "^0.7.43",
|
||||
"ioredis": "^5.4.1",
|
||||
"lucide-svelte": "^0.462.0",
|
||||
"lucide-svelte": "^0.468.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"svelte-local-storage-store": "^0.6.4"
|
||||
}
|
||||
|
|
|
|||
1435
pnpm-lock.yaml
1435
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
|
@ -1,28 +1,34 @@
|
|||
<script lang="ts">
|
||||
import { Mail } from 'lucide-svelte';
|
||||
import { blueSkyIcon, gitHubIcon, linkedInIcon, xIcon } from '../util/logoIcons.svelte';
|
||||
import { Mail } from "lucide-svelte";
|
||||
import {
|
||||
blueSkyIcon,
|
||||
gitHubIcon,
|
||||
linkedInIcon,
|
||||
xIcon,
|
||||
} from "../util/logoIcons.svelte";
|
||||
import ExternalLink from '$lib/components/ExternalLink.svelte';
|
||||
|
||||
interface Props {
|
||||
showBlueSky?: boolean;
|
||||
showEmail?: boolean;
|
||||
showGithub?: boolean;
|
||||
showLinkedIn?: boolean;
|
||||
showX?: boolean;
|
||||
userNames: Record<string, string>;
|
||||
showText?: boolean;
|
||||
justify?: boolean;
|
||||
}
|
||||
showBlueSky?: boolean;
|
||||
showEmail?: boolean;
|
||||
showGithub?: boolean;
|
||||
showLinkedIn?: boolean;
|
||||
showX?: boolean;
|
||||
userNames: Record<string, string>;
|
||||
showText?: boolean;
|
||||
justify?: boolean;
|
||||
}
|
||||
|
||||
let {
|
||||
showBlueSky = false,
|
||||
showEmail = false,
|
||||
showGithub = false,
|
||||
showLinkedIn = false,
|
||||
showX = false,
|
||||
userNames,
|
||||
showText = false,
|
||||
justify = false,
|
||||
}: Props = $props();
|
||||
showBlueSky = false,
|
||||
showEmail = false,
|
||||
showGithub = false,
|
||||
showLinkedIn = false,
|
||||
showX = false,
|
||||
userNames,
|
||||
showText = false,
|
||||
justify = false,
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
{#if showText}
|
||||
|
|
@ -30,129 +36,94 @@
|
|||
{/if}
|
||||
<div class:justifyCenter={justify}>
|
||||
{#if showX && userNames?.x}
|
||||
<a
|
||||
href={`https://www.x.com/${userNames.x}`}
|
||||
target="_blank"
|
||||
title="Contact through X"
|
||||
aria-label="Contact through X"
|
||||
rel="noreferrer"
|
||||
class="hub-icon"
|
||||
>
|
||||
{@render xIcon('x-contact')}
|
||||
</a>
|
||||
<ExternalLink
|
||||
linkData={{ href: `https://www.x.com/${userNames.x}`, ariaLabel: 'Contact through X', title: 'Contact through X', target: '_blank', clazz: "hub-icon x-contact" }}
|
||||
iconData={{ type: 'svg', icon: xIcon, iconClass: 'center' }}
|
||||
textData={{ showIcon: true }}
|
||||
/>
|
||||
{/if}
|
||||
{#if showBlueSky && userNames?.blueSky}
|
||||
<a
|
||||
href={`https://bsky.app/profile/${userNames.blueSky}`}
|
||||
target="_blank"
|
||||
title="Contact through Bluesky"
|
||||
aria-label="Contact through Bluesky"
|
||||
rel="noreferrer"
|
||||
class="hub-icon"
|
||||
>
|
||||
{@render blueSkyIcon('bluesky-contact')}
|
||||
</a>
|
||||
<ExternalLink
|
||||
linkData={{ href: `https://bsky.app/profile/${userNames.blueSky}`, ariaLabel: 'Contact through Bluesky', title: 'Contact through Bluesky', target: '_blank', clazz: "hub-icon bluesky-contact" }}
|
||||
iconData={{ type: 'svg', icon: blueSkyIcon, iconClass: 'center' }}
|
||||
textData={{ showIcon: true }}
|
||||
/>
|
||||
{/if}
|
||||
{#if showLinkedIn && userNames?.linkedIn}
|
||||
<a
|
||||
href={`https://www.linkedin.com/in/${userNames.linkedIn}`}
|
||||
target="_blank"
|
||||
title="Contact through LinkedIn"
|
||||
aria-label="Contact through LinkedIn"
|
||||
rel="noreferrer"
|
||||
class="hub-icon"
|
||||
>
|
||||
{@render linkedInIcon('linkedIn-contact')}
|
||||
</a>
|
||||
{/if}
|
||||
<ExternalLink
|
||||
linkData={{ href: `https://www.linkedin.com/in/${userNames.linkedIn}`, ariaLabel: 'Contact through LinkedIn', title: 'Contact through LinkedIn', target: '_blank', clazz: "hub-icon linkedIn-contact" }}
|
||||
iconData={{ type: 'svg', icon: linkedInIcon, iconClass: 'center' }}
|
||||
textData={{ showIcon: true }}
|
||||
/>
|
||||
{/if}
|
||||
{#if showGithub && userNames?.github}
|
||||
<a
|
||||
href={`https://www.github.com/${userNames.github}`}
|
||||
target="_blank"
|
||||
title="View Github"
|
||||
aria-label="View Github"
|
||||
rel="noreferrer"
|
||||
class="hub-icon github-contact"
|
||||
>
|
||||
{@render gitHubIcon('github-contact')}
|
||||
</a>
|
||||
{/if}
|
||||
<ExternalLink
|
||||
linkData={{ href: `https://www.github.com/${userNames.github}`, ariaLabel: 'Contact through Github', title: 'Contact through Github', target: '_blank', clazz: "hub-icon github-contact" }}
|
||||
iconData={{ type: 'svg', icon: gitHubIcon, iconClass: 'center' }}
|
||||
textData={{ showIcon: true }}
|
||||
/>
|
||||
{/if}
|
||||
{#if showEmail && userNames?.email}
|
||||
<a
|
||||
href={`mailto:${userNames.email}`}
|
||||
target="_blank"
|
||||
title="Contact by email"
|
||||
aria-label="Contact by email"
|
||||
rel="noreferrer"
|
||||
class="email-contact hub-icon"
|
||||
>
|
||||
<Mail size="25" />
|
||||
</a>
|
||||
{/if}
|
||||
<ExternalLink
|
||||
linkData={{ href: `mailto:${userNames.email}`, ariaLabel: 'Contact by email', title: 'Contact by email', target: '_blank', clazz: "hub-icon email-contact" }}
|
||||
iconData={{ type: 'icon', icon: Mail, iconClass: 'center' }}
|
||||
textData={{ showIcon: true }}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
div {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2rem;
|
||||
align-items: center;
|
||||
|
||||
&.justifyCenter {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
--xColor: #000000;
|
||||
--linkedInColor: #0077B5;
|
||||
--githubColor: #000000;
|
||||
--blueskyColor: #0085FF;
|
||||
--emailColor: var(--linkHover);
|
||||
}
|
||||
|
||||
.hub-icon {
|
||||
transition: transform 0.2s cubic-bezier(0.65, 0, 0.35, 1);
|
||||
&:hover {
|
||||
transition: transform 0.4s cubic-bezier(0.65, 0, 0.35, 1);
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 4rem;
|
||||
margin: 1.5rem;
|
||||
|
||||
@media (max-width: 550px) {
|
||||
font-size: 3.55rem;
|
||||
}
|
||||
}
|
||||
|
||||
.x-contact {
|
||||
color: var(--textColor);
|
||||
&:hover {
|
||||
color: var(--twitterColor);
|
||||
}
|
||||
}
|
||||
.linkedIn-contact {
|
||||
color: var(--textColor);
|
||||
&:hover {
|
||||
color: var(--linkedInColor);
|
||||
}
|
||||
}
|
||||
.github-contact {
|
||||
color: var(--textColor);
|
||||
&:hover {
|
||||
color: var(--githubColor);
|
||||
}
|
||||
:global(.hub-icon) {
|
||||
transition: transform 0.2s cubic-bezier(0.65, 0, 0.35, 1);
|
||||
&:hover {
|
||||
transition: transform 0.4s cubic-bezier(0.65, 0, 0.35, 1);
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
}
|
||||
.bluesky-contact {
|
||||
|
||||
:global(.x-contact) {
|
||||
color: var(--textColor);
|
||||
&:hover {
|
||||
color: var(--xColor);
|
||||
}
|
||||
}
|
||||
|
||||
:global(.linkedIn-contact) {
|
||||
color: var(--textColor);
|
||||
&:hover {
|
||||
color: var(--linkedInColor);
|
||||
}
|
||||
}
|
||||
|
||||
:global(.github-contact) {
|
||||
color: var(--textColor);
|
||||
&:hover {
|
||||
color: var(--githubColor);
|
||||
}
|
||||
}
|
||||
|
||||
:global(.bluesky-contact) {
|
||||
color: var(--textColor);
|
||||
&:hover {
|
||||
color: var(--blueskyColor);
|
||||
}
|
||||
}
|
||||
.email-contact {
|
||||
color: var(--textColor);
|
||||
&:hover {
|
||||
color: var(--linkHover);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
}
|
||||
|
||||
:global(.email-contact) {
|
||||
color: var(--textColor);
|
||||
&:hover {
|
||||
color: var(--linkHover);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,47 +1,112 @@
|
|||
<script lang="ts">
|
||||
import { ExternalLink,type Icon as IconType } from 'lucide-svelte';
|
||||
import { ExternalLink } from "lucide-svelte";
|
||||
import type {
|
||||
ExternalLinkType,
|
||||
LinkIconType,
|
||||
} from "$lib/types/externalLinkTypes";
|
||||
|
||||
interface Props {
|
||||
rel?: string;
|
||||
target?: string;
|
||||
href: string;
|
||||
ariaLabel: string;
|
||||
showIcon?: boolean;
|
||||
clazz?: string;
|
||||
icon?: typeof IconType | string;
|
||||
children?: import('svelte').Snippet;
|
||||
const { iconData, linkData, textData }: ExternalLinkType = $props();
|
||||
|
||||
let textLocationClass = "";
|
||||
if (textData?.location === "top") {
|
||||
textLocationClass = "text-top";
|
||||
} else if (textData?.location === "bottom") {
|
||||
textLocationClass = "text-bottom";
|
||||
} else if (textData?.location === "left") {
|
||||
textLocationClass = "text-left";
|
||||
} else if (textData?.location === "right") {
|
||||
textLocationClass = "text-right";
|
||||
}
|
||||
|
||||
let { rel = 'noreferrer', target = '_blank', href, ariaLabel, showIcon = false, clazz = '', icon = ExternalLink, children }: Props = $props();
|
||||
const linkDecoration =
|
||||
linkData?.textDecoration && linkData?.textDecoration === "none"
|
||||
? `text-decoration-${linkData?.textDecoration}`
|
||||
: "text-decoration-underline";
|
||||
const linkClass =
|
||||
`${linkData?.clazz} ${textLocationClass} ${linkDecoration}`.trim();
|
||||
</script>
|
||||
|
||||
|
||||
<a class:show-icon={showIcon} class={clazz} aria-label={`Open ${ariaLabel} externally`} title={`Open ${ariaLabel} externally`} {href} {rel} {target}>
|
||||
{@render children?.()}
|
||||
{#if showIcon}
|
||||
{#if typeof icon === 'string'}
|
||||
{@html icon}
|
||||
{:else}
|
||||
{@const Icon = icon}
|
||||
<Icon />
|
||||
{#snippet externalLink({ iconData, linkData, textData }: ExternalLinkType)}
|
||||
<a
|
||||
class={linkClass}
|
||||
aria-label={`Open ${linkData?.ariaLabel ?? linkData?.title ?? linkData?.href} externally`}
|
||||
title={linkData?.title ?? `Open ${linkData?.ariaLabel} externally`}
|
||||
href={linkData.href}
|
||||
rel={linkData?.rel ?? "noreferrer"}
|
||||
target={linkData?.target ?? "_blank"}
|
||||
>
|
||||
{#if textData?.location === "top" || (textData?.location === "left" && textData?.text)}
|
||||
<span class="link-text">{textData?.text}</span>
|
||||
{/if}
|
||||
{#if textData?.showIcon}
|
||||
{@render linkIcon?.(iconData ?? {})}
|
||||
{/if}
|
||||
{#if textData?.location === "bottom" || (textData?.location === "right" && textData?.text)}
|
||||
<span class="link-text">{textData?.text}</span>
|
||||
{/if}
|
||||
</a>
|
||||
{/snippet}
|
||||
|
||||
{#snippet linkIcon({ type, icon, iconClass }: LinkIconType)}
|
||||
{#if type === "svg" && icon}
|
||||
<svg
|
||||
style="width: 2.5rem; height: 2.5rem;"
|
||||
class={iconClass ?? ""}
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
{@render icon?.()}
|
||||
</svg>
|
||||
{:else if type === "icon" && icon}
|
||||
{@const Icon = icon}
|
||||
<Icon />
|
||||
{:else}
|
||||
{@const Icon = ExternalLink}
|
||||
<Icon />
|
||||
{/if}
|
||||
</a>
|
||||
{/snippet}
|
||||
|
||||
{@render externalLink({ iconData, linkData, textData })}
|
||||
|
||||
<style lang="postcss">
|
||||
a {
|
||||
margin: 1rem 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 0;
|
||||
font-size: var(--bodyTextSize);
|
||||
}
|
||||
|
||||
.show-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
.text-top {
|
||||
padding-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.text-bottom {
|
||||
padding-top: 0.3rem;
|
||||
}
|
||||
|
||||
.text-left,
|
||||
.text-right {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.text-decoration-none {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.text-decoration-underline {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.show-icon {
|
||||
/* display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem; */
|
||||
|
||||
&:hover {
|
||||
color: var(--shellYellow);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
<style lang="postcss">
|
||||
footer {
|
||||
display: grid;
|
||||
gap: 2rem;
|
||||
grid-template-rows: repeat(3, 1fr);
|
||||
background: var(--footerBackground);
|
||||
place-content: center;
|
||||
|
|
|
|||
30
src/lib/types/externalLinkTypes.ts
Normal file
30
src/lib/types/externalLinkTypes.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import type { Snippet } from 'svelte';
|
||||
import type { Icon as IconType } from 'lucide-svelte';
|
||||
|
||||
export interface LinkTextType {
|
||||
text?: string;
|
||||
showIcon: boolean;
|
||||
location?: 'top' | 'bottom' | 'left' | 'right';
|
||||
}
|
||||
|
||||
export interface LinkDataType {
|
||||
rel?: string;
|
||||
target?: string;
|
||||
href: string;
|
||||
title?: string;
|
||||
ariaLabel: string;
|
||||
clazz?: string | undefined;
|
||||
textDecoration?: 'none' | 'underline' | 'line-through';
|
||||
}
|
||||
|
||||
export interface ExternalLinkType {
|
||||
iconData?: LinkIconType;
|
||||
linkData: LinkDataType;
|
||||
textData?: LinkTextType;
|
||||
}
|
||||
|
||||
export interface LinkIconType {
|
||||
type?: 'icon' | 'svg';
|
||||
icon?: Snippet | typeof IconType;
|
||||
iconClass?: string | undefined;
|
||||
}
|
||||
|
|
@ -1,54 +1,53 @@
|
|||
<script module>
|
||||
export { blueSkyIcon, dockerIcon, drizzleIcon, honoIcon, gitHubIcon, linkedInIcon, nextDotJsIcon, reactIcon, svelteIcon, typescriptIcon, xIcon };
|
||||
import { ExternalLink } from 'lucide-svelte';
|
||||
export { blueSkyIcon, dockerIcon, drizzleIcon, honoIcon, gitHubIcon, linkedInIcon, lucideIcon, nextDotJsIcon, reactIcon, svelteIcon, typescriptIcon, xIcon };
|
||||
</script>
|
||||
|
||||
{#snippet gitHubIcon(clazz)}
|
||||
<svg class={clazz} role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
|
||||
{#snippet gitHubIcon()}
|
||||
<path fill="currentColor" d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/>
|
||||
{/snippet}
|
||||
|
||||
{#snippet drizzleIcon(clazz, style)}
|
||||
<svg class={clazz} {style} role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Drizzle</title><path d="M5.353 11.823a1.036 1.036 0 0 0-.395-1.422 1.063 1.063 0 0 0-1.437.399L.138 16.702a1.035 1.035 0 0 0 .395 1.422 1.063 1.063 0 0 0 1.437-.398l3.383-5.903Zm11.216 0a1.036 1.036 0 0 0-.394-1.422 1.064 1.064 0 0 0-1.438.399l-3.382 5.902a1.036 1.036 0 0 0 .394 1.422c.506.283 1.15.104 1.438-.398l3.382-5.903Zm7.293-4.525a1.036 1.036 0 0 0-.395-1.422 1.062 1.062 0 0 0-1.437.399l-3.383 5.902a1.036 1.036 0 0 0 .395 1.422 1.063 1.063 0 0 0 1.437-.399l3.383-5.902Zm-11.219 0a1.035 1.035 0 0 0-.394-1.422 1.064 1.064 0 0 0-1.438.398l-3.382 5.903a1.036 1.036 0 0 0 .394 1.422c.506.282 1.15.104 1.438-.399l3.382-5.902Z"/></svg>
|
||||
{#snippet drizzleIcon()}
|
||||
<path fill="currentColor" d="M5.353 11.823a1.036 1.036 0 0 0-.395-1.422 1.063 1.063 0 0 0-1.437.399L.138 16.702a1.035 1.035 0 0 0 .395 1.422 1.063 1.063 0 0 0 1.437-.398l3.383-5.903Zm11.216 0a1.036 1.036 0 0 0-.394-1.422 1.064 1.064 0 0 0-1.438.399l-3.382 5.902a1.036 1.036 0 0 0 .394 1.422c.506.283 1.15.104 1.438-.398l3.382-5.903Zm7.293-4.525a1.036 1.036 0 0 0-.395-1.422 1.062 1.062 0 0 0-1.437.399l-3.383 5.902a1.036 1.036 0 0 0 .395 1.422 1.063 1.063 0 0 0 1.437-.399l3.383-5.902Zm-11.219 0a1.035 1.035 0 0 0-.394-1.422 1.064 1.064 0 0 0-1.438.398l-3.382 5.903a1.036 1.036 0 0 0 .394 1.422c.506.282 1.15.104 1.438-.399l3.382-5.902Z"/>
|
||||
{/snippet}
|
||||
|
||||
{#snippet svelteIcon(clazz)}
|
||||
<svg class={clazz} role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Svelte</title><path d="M10.354 21.125a4.44 4.44 0 0 1-4.765-1.767 4.109 4.109 0 0 1-.703-3.107 3.898 3.898 0 0 1 .134-.522l.105-.321.287.21a7.21 7.21 0 0 0 2.186 1.092l.208.063-.02.208a1.253 1.253 0 0 0 .226.83 1.337 1.337 0 0 0 1.435.533 1.231 1.231 0 0 0 .343-.15l5.59-3.562a1.164 1.164 0 0 0 .524-.778 1.242 1.242 0 0 0-.211-.937 1.338 1.338 0 0 0-1.435-.533 1.23 1.23 0 0 0-.343.15l-2.133 1.36a4.078 4.078 0 0 1-1.135.499 4.44 4.44 0 0 1-4.765-1.766 4.108 4.108 0 0 1-.702-3.108 3.855 3.855 0 0 1 1.742-2.582l5.589-3.563a4.072 4.072 0 0 1 1.135-.499 4.44 4.44 0 0 1 4.765 1.767 4.109 4.109 0 0 1 .703 3.107 3.943 3.943 0 0 1-.134.522l-.105.321-.286-.21a7.204 7.204 0 0 0-2.187-1.093l-.208-.063.02-.207a1.255 1.255 0 0 0-.226-.831 1.337 1.337 0 0 0-1.435-.532 1.231 1.231 0 0 0-.343.15L8.62 9.368a1.162 1.162 0 0 0-.524.778 1.24 1.24 0 0 0 .211.937 1.338 1.338 0 0 0 1.435.533 1.235 1.235 0 0 0 .344-.151l2.132-1.36a4.067 4.067 0 0 1 1.135-.498 4.44 4.44 0 0 1 4.765 1.766 4.108 4.108 0 0 1 .702 3.108 3.857 3.857 0 0 1-1.742 2.583l-5.589 3.562a4.072 4.072 0 0 1-1.135.499m10.358-17.95C18.484-.015 14.082-.96 10.9 1.068L5.31 4.63a6.412 6.412 0 0 0-2.896 4.295 6.753 6.753 0 0 0 .666 4.336 6.43 6.43 0 0 0-.96 2.396 6.833 6.833 0 0 0 1.168 5.167c2.229 3.19 6.63 4.135 9.812 2.108l5.59-3.562a6.41 6.41 0 0 0 2.896-4.295 6.756 6.756 0 0 0-.665-4.336 6.429 6.429 0 0 0 .958-2.396 6.831 6.831 0 0 0-1.167-5.168Z"/></svg>
|
||||
{#snippet svelteIcon()}
|
||||
<path fill="currentColor" d="M10.354 21.125a4.44 4.44 0 0 1-4.765-1.767 4.109 4.109 0 0 1-.703-3.107 3.898 3.898 0 0 1 .134-.522l.105-.321.287.21a7.21 7.21 0 0 0 2.186 1.092l.208.063-.02.208a1.253 1.253 0 0 0 .226.83 1.337 1.337 0 0 0 1.435.533 1.231 1.231 0 0 0 .343-.15l5.59-3.562a1.164 1.164 0 0 0 .524-.778 1.242 1.242 0 0 0-.211-.937 1.338 1.338 0 0 0-1.435-.533 1.23 1.23 0 0 0-.343.15l-2.133 1.36a4.078 4.078 0 0 1-1.135.499 4.44 4.44 0 0 1-4.765-1.766 4.108 4.108 0 0 1-.702-3.108 3.855 3.855 0 0 1 1.742-2.582l5.589-3.563a4.072 4.072 0 0 1 1.135-.499 4.44 4.44 0 0 1 4.765 1.767 4.109 4.109 0 0 1 .703 3.107 3.943 3.943 0 0 1-.134.522l-.105.321-.286-.21a7.204 7.204 0 0 0-2.187-1.093l-.208-.063.02-.207a1.255 1.255 0 0 0-.226-.831 1.337 1.337 0 0 0-1.435-.532 1.231 1.231 0 0 0-.343.15L8.62 9.368a1.162 1.162 0 0 0-.524.778 1.24 1.24 0 0 0 .211.937 1.338 1.338 0 0 0 1.435.533 1.235 1.235 0 0 0 .344-.151l2.132-1.36a4.067 4.067 0 0 1 1.135-.498 4.44 4.44 0 0 1 4.765 1.766 4.108 4.108 0 0 1 .702 3.108 3.857 3.857 0 0 1-1.742 2.583l-5.589 3.562a4.072 4.072 0 0 1-1.135.499m10.358-17.95C18.484-.015 14.082-.96 10.9 1.068L5.31 4.63a6.412 6.412 0 0 0-2.896 4.295 6.753 6.753 0 0 0 .666 4.336 6.43 6.43 0 0 0-.96 2.396 6.833 6.833 0 0 0 1.168 5.167c2.229 3.19 6.63 4.135 9.812 2.108l5.59-3.562a6.41 6.41 0 0 0 2.896-4.295 6.756 6.756 0 0 0-.665-4.336 6.429 6.429 0 0 0 .958-2.396 6.831 6.831 0 0 0-1.167-5.168Z"/>
|
||||
{/snippet}
|
||||
|
||||
{#snippet typescriptIcon(clazz)}
|
||||
<svg class={clazz} role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>TypeScript</title><path d="M1.125 0C.502 0 0 .502 0 1.125v21.75C0 23.498.502 24 1.125 24h21.75c.623 0 1.125-.502 1.125-1.125V1.125C24 .502 23.498 0 22.875 0zm17.363 9.75c.612 0 1.154.037 1.627.111a6.38 6.38 0 0 1 1.306.34v2.458a3.95 3.95 0 0 0-.643-.361 5.093 5.093 0 0 0-.717-.26 5.453 5.453 0 0 0-1.426-.2c-.3 0-.573.028-.819.086a2.1 2.1 0 0 0-.623.242c-.17.104-.3.229-.393.374a.888.888 0 0 0-.14.49c0 .196.053.373.156.529.104.156.252.304.443.444s.423.276.696.41c.273.135.582.274.926.416.47.197.892.407 1.266.628.374.222.695.473.963.753.268.279.472.598.614.957.142.359.214.776.214 1.253 0 .657-.125 1.21-.373 1.656a3.033 3.033 0 0 1-1.012 1.085 4.38 4.38 0 0 1-1.487.596c-.566.12-1.163.18-1.79.18a9.916 9.916 0 0 1-1.84-.164 5.544 5.544 0 0 1-1.512-.493v-2.63a5.033 5.033 0 0 0 3.237 1.2c.333 0 .624-.03.872-.09.249-.06.456-.144.623-.25.166-.108.29-.234.373-.38a1.023 1.023 0 0 0-.074-1.089 2.12 2.12 0 0 0-.537-.5 5.597 5.597 0 0 0-.807-.444 27.72 27.72 0 0 0-1.007-.436c-.918-.383-1.602-.852-2.053-1.405-.45-.553-.676-1.222-.676-2.005 0-.614.123-1.141.369-1.582.246-.441.58-.804 1.004-1.089a4.494 4.494 0 0 1 1.47-.629 7.536 7.536 0 0 1 1.77-.201zm-15.113.188h9.563v2.166H9.506v9.646H6.789v-9.646H3.375z"/></svg>
|
||||
{#snippet typescriptIcon()}
|
||||
<path fill="currentColor" d="M1.125 0C.502 0 0 .502 0 1.125v21.75C0 23.498.502 24 1.125 24h21.75c.623 0 1.125-.502 1.125-1.125V1.125C24 .502 23.498 0 22.875 0zm17.363 9.75c.612 0 1.154.037 1.627.111a6.38 6.38 0 0 1 1.306.34v2.458a3.95 3.95 0 0 0-.643-.361 5.093 5.093 0 0 0-.717-.26 5.453 5.453 0 0 0-1.426-.2c-.3 0-.573.028-.819.086a2.1 2.1 0 0 0-.623.242c-.17.104-.3.229-.393.374a.888.888 0 0 0-.14.49c0 .196.053.373.156.529.104.156.252.304.443.444s.423.276.696.41c.273.135.582.274.926.416.47.197.892.407 1.266.628.374.222.695.473.963.753.268.279.472.598.614.957.142.359.214.776.214 1.253 0 .657-.125 1.21-.373 1.656a3.033 3.033 0 0 1-1.012 1.085 4.38 4.38 0 0 1-1.487.596c-.566.12-1.163.18-1.79.18a9.916 9.916 0 0 1-1.84-.164 5.544 5.544 0 0 1-1.512-.493v-2.63a5.033 5.033 0 0 0 3.237 1.2c.333 0 .624-.03.872-.09.249-.06.456-.144.623-.25.166-.108.29-.234.373-.38a1.023 1.023 0 0 0-.074-1.089 2.12 2.12 0 0 0-.537-.5 5.597 5.597 0 0 0-.807-.444 27.72 27.72 0 0 0-1.007-.436c-.918-.383-1.602-.852-2.053-1.405-.45-.553-.676-1.222-.676-2.005 0-.614.123-1.141.369-1.582.246-.441.58-.804 1.004-1.089a4.494 4.494 0 0 1 1.47-.629 7.536 7.536 0 0 1 1.77-.201zm-15.113.188h9.563v2.166H9.506v9.646H6.789v-9.646H3.375z"/>
|
||||
{/snippet}
|
||||
|
||||
{#snippet reactIcon(clazz)}
|
||||
<svg class={clazz} role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>React</title><path d="M14.23 12.004a2.236 2.236 0 0 1-2.235 2.236 2.236 2.236 0 0 1-2.236-2.236 2.236 2.236 0 0 1 2.235-2.236 2.236 2.236 0 0 1 2.236 2.236zm2.648-10.69c-1.346 0-3.107.96-4.888 2.622-1.78-1.653-3.542-2.602-4.887-2.602-.41 0-.783.093-1.106.278-1.375.793-1.683 3.264-.973 6.365C1.98 8.917 0 10.42 0 12.004c0 1.59 1.99 3.097 5.043 4.03-.704 3.113-.39 5.588.988 6.38.32.187.69.275 1.102.275 1.345 0 3.107-.96 4.888-2.624 1.78 1.654 3.542 2.603 4.887 2.603.41 0 .783-.09 1.106-.275 1.374-.792 1.683-3.263.973-6.365C22.02 15.096 24 13.59 24 12.004c0-1.59-1.99-3.097-5.043-4.032.704-3.11.39-5.587-.988-6.38-.318-.184-.688-.277-1.092-.278zm-.005 1.09v.006c.225 0 .406.044.558.127.666.382.955 1.835.73 3.704-.054.46-.142.945-.25 1.44-.96-.236-2.006-.417-3.107-.534-.66-.905-1.345-1.727-2.035-2.447 1.592-1.48 3.087-2.292 4.105-2.295zm-9.77.02c1.012 0 2.514.808 4.11 2.28-.686.72-1.37 1.537-2.02 2.442-1.107.117-2.154.298-3.113.538-.112-.49-.195-.964-.254-1.42-.23-1.868.054-3.32.714-3.707.19-.09.4-.127.563-.132zm4.882 3.05c.455.468.91.992 1.36 1.564-.44-.02-.89-.034-1.345-.034-.46 0-.915.01-1.36.034.44-.572.895-1.096 1.345-1.565zM12 8.1c.74 0 1.477.034 2.202.093.406.582.802 1.203 1.183 1.86.372.64.71 1.29 1.018 1.946-.308.655-.646 1.31-1.013 1.95-.38.66-.773 1.288-1.18 1.87-.728.063-1.466.098-2.21.098-.74 0-1.477-.035-2.202-.093-.406-.582-.802-1.204-1.183-1.86-.372-.64-.71-1.29-1.018-1.946.303-.657.646-1.313 1.013-1.954.38-.66.773-1.286 1.18-1.868.728-.064 1.466-.098 2.21-.098zm-3.635.254c-.24.377-.48.763-.704 1.16-.225.39-.435.782-.635 1.174-.265-.656-.49-1.31-.676-1.947.64-.15 1.315-.283 2.015-.386zm7.26 0c.695.103 1.365.23 2.006.387-.18.632-.405 1.282-.66 1.933-.2-.39-.41-.783-.64-1.174-.225-.392-.465-.774-.705-1.146zm3.063.675c.484.15.944.317 1.375.498 1.732.74 2.852 1.708 2.852 2.476-.005.768-1.125 1.74-2.857 2.475-.42.18-.88.342-1.355.493-.28-.958-.646-1.956-1.1-2.98.45-1.017.81-2.01 1.085-2.964zm-13.395.004c.278.96.645 1.957 1.1 2.98-.45 1.017-.812 2.01-1.086 2.964-.484-.15-.944-.318-1.37-.5-1.732-.737-2.852-1.706-2.852-2.474 0-.768 1.12-1.742 2.852-2.476.42-.18.88-.342 1.356-.494zm11.678 4.28c.265.657.49 1.312.676 1.948-.64.157-1.316.29-2.016.39.24-.375.48-.762.705-1.158.225-.39.435-.788.636-1.18zm-9.945.02c.2.392.41.783.64 1.175.23.39.465.772.705 1.143-.695-.102-1.365-.23-2.006-.386.18-.63.406-1.282.66-1.933zM17.92 16.32c.112.493.2.968.254 1.423.23 1.868-.054 3.32-.714 3.708-.147.09-.338.128-.563.128-1.012 0-2.514-.807-4.11-2.28.686-.72 1.37-1.536 2.02-2.44 1.107-.118 2.154-.3 3.113-.54zm-11.83.01c.96.234 2.006.415 3.107.532.66.905 1.345 1.727 2.035 2.446-1.595 1.483-3.092 2.295-4.11 2.295-.22-.005-.406-.05-.553-.132-.666-.38-.955-1.834-.73-3.703.054-.46.142-.944.25-1.438zm4.56.64c.44.02.89.034 1.345.034.46 0 .915-.01 1.36-.034-.44.572-.895 1.095-1.345 1.565-.455-.47-.91-.993-1.36-1.565z"/></svg>
|
||||
{#snippet reactIcon()}
|
||||
<path fill="currentColor" d="M14.23 12.004a2.236 2.236 0 0 1-2.235 2.236 2.236 2.236 0 0 1-2.236-2.236 2.236 2.236 0 0 1 2.235-2.236 2.236 2.236 0 0 1 2.236 2.236zm2.648-10.69c-1.346 0-3.107.96-4.888 2.622-1.78-1.653-3.542-2.602-4.887-2.602-.41 0-.783.093-1.106.278-1.375.793-1.683 3.264-.973 6.365C1.98 8.917 0 10.42 0 12.004c0 1.59 1.99 3.097 5.043 4.03-.704 3.113-.39 5.588.988 6.38.32.187.69.275 1.102.275 1.345 0 3.107-.96 4.888-2.624 1.78 1.654 3.542 2.603 4.887 2.603.41 0 .783-.09 1.106-.275 1.374-.792 1.683-3.263.973-6.365C22.02 15.096 24 13.59 24 12.004c0-1.59-1.99-3.097-5.043-4.032.704-3.11.39-5.587-.988-6.38-.318-.184-.688-.277-1.092-.278zm-.005 1.09v.006c.225 0 .406.044.558.127.666.382.955 1.835.73 3.704-.054.46-.142.945-.25 1.44-.96-.236-2.006-.417-3.107-.534-.66-.905-1.345-1.727-2.035-2.447 1.592-1.48 3.087-2.292 4.105-2.295zm-9.77.02c1.012 0 2.514.808 4.11 2.28-.686.72-1.37 1.537-2.02 2.442-1.107.117-2.154.298-3.113.538-.112-.49-.195-.964-.254-1.42-.23-1.868.054-3.32.714-3.707.19-.09.4-.127.563-.132zm4.882 3.05c.455.468.91.992 1.36 1.564-.44-.02-.89-.034-1.345-.034-.46 0-.915.01-1.36.034.44-.572.895-1.096 1.345-1.565zM12 8.1c.74 0 1.477.034 2.202.093.406.582.802 1.203 1.183 1.86.372.64.71 1.29 1.018 1.946-.308.655-.646 1.31-1.013 1.95-.38.66-.773 1.288-1.18 1.87-.728.063-1.466.098-2.21.098-.74 0-1.477-.035-2.202-.093-.406-.582-.802-1.204-1.183-1.86-.372-.64-.71-1.29-1.018-1.946.303-.657.646-1.313 1.013-1.954.38-.66.773-1.286 1.18-1.868.728-.064 1.466-.098 2.21-.098zm-3.635.254c-.24.377-.48.763-.704 1.16-.225.39-.435.782-.635 1.174-.265-.656-.49-1.31-.676-1.947.64-.15 1.315-.283 2.015-.386zm7.26 0c.695.103 1.365.23 2.006.387-.18.632-.405 1.282-.66 1.933-.2-.39-.41-.783-.64-1.174-.225-.392-.465-.774-.705-1.146zm3.063.675c.484.15.944.317 1.375.498 1.732.74 2.852 1.708 2.852 2.476-.005.768-1.125 1.74-2.857 2.475-.42.18-.88.342-1.355.493-.28-.958-.646-1.956-1.1-2.98.45-1.017.81-2.01 1.085-2.964zm-13.395.004c.278.96.645 1.957 1.1 2.98-.45 1.017-.812 2.01-1.086 2.964-.484-.15-.944-.318-1.37-.5-1.732-.737-2.852-1.706-2.852-2.474 0-.768 1.12-1.742 2.852-2.476.42-.18.88-.342 1.356-.494zm11.678 4.28c.265.657.49 1.312.676 1.948-.64.157-1.316.29-2.016.39.24-.375.48-.762.705-1.158.225-.39.435-.788.636-1.18zm-9.945.02c.2.392.41.783.64 1.175.23.39.465.772.705 1.143-.695-.102-1.365-.23-2.006-.386.18-.63.406-1.282.66-1.933zM17.92 16.32c.112.493.2.968.254 1.423.23 1.868-.054 3.32-.714 3.708-.147.09-.338.128-.563.128-1.012 0-2.514-.807-4.11-2.28.686-.72 1.37-1.536 2.02-2.44 1.107-.118 2.154-.3 3.113-.54zm-11.83.01c.96.234 2.006.415 3.107.532.66.905 1.345 1.727 2.035 2.446-1.595 1.483-3.092 2.295-4.11 2.295-.22-.005-.406-.05-.553-.132-.666-.38-.955-1.834-.73-3.703.054-.46.142-.944.25-1.438zm4.56.64c.44.02.89.034 1.345.034.46 0 .915-.01 1.36-.034-.44.572-.895 1.095-1.345 1.565-.455-.47-.91-.993-1.36-1.565z"/>
|
||||
{/snippet}
|
||||
|
||||
{#snippet dockerIcon(clazz)}
|
||||
<svg class={clazz} role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Docker</title><path d="M13.983 11.078h2.119a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.119a.185.185 0 00-.185.185v1.888c0 .102.083.185.185.185m-2.954-5.43h2.118a.186.186 0 00.186-.186V3.574a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m0 2.716h2.118a.187.187 0 00.186-.186V6.29a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.887c0 .102.082.185.185.186m-2.93 0h2.12a.186.186 0 00.184-.186V6.29a.185.185 0 00-.185-.185H8.1a.185.185 0 00-.185.185v1.887c0 .102.083.185.185.186m-2.964 0h2.119a.186.186 0 00.185-.186V6.29a.185.185 0 00-.185-.185H5.136a.186.186 0 00-.186.185v1.887c0 .102.084.185.186.186m5.893 2.715h2.118a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m-2.93 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.083.185.185.185m-2.964 0h2.119a.185.185 0 00.185-.185V9.006a.185.185 0 00-.184-.186h-2.12a.186.186 0 00-.186.186v1.887c0 .102.084.185.186.185m-2.92 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.082.185.185.185M23.763 9.89c-.065-.051-.672-.51-1.954-.51-.338.001-.676.03-1.01.087-.248-1.7-1.653-2.53-1.716-2.566l-.344-.199-.226.327c-.284.438-.49.922-.612 1.43-.23.97-.09 1.882.403 2.661-.595.332-1.55.413-1.744.42H.751a.751.751 0 00-.75.748 11.376 11.376 0 00.692 4.062c.545 1.428 1.355 2.48 2.41 3.124 1.18.723 3.1 1.137 5.275 1.137.983.003 1.963-.086 2.93-.266a12.248 12.248 0 003.823-1.389c.98-.567 1.86-1.288 2.61-2.136 1.252-1.418 1.998-2.997 2.553-4.4h.221c1.372 0 2.215-.549 2.68-1.009.309-.293.55-.65.707-1.046l.098-.288Z"/></svg>
|
||||
{#snippet dockerIcon()}
|
||||
<path fill="currentColor" d="M13.983 11.078h2.119a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.119a.185.185 0 00-.185.185v1.888c0 .102.083.185.185.185m-2.954-5.43h2.118a.186.186 0 00.186-.186V3.574a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m0 2.716h2.118a.187.187 0 00.186-.186V6.29a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.887c0 .102.082.185.185.186m-2.93 0h2.12a.186.186 0 00.184-.186V6.29a.185.185 0 00-.185-.185H8.1a.185.185 0 00-.185.185v1.887c0 .102.083.185.185.186m-2.964 0h2.119a.186.186 0 00.185-.186V6.29a.185.185 0 00-.185-.185H5.136a.186.186 0 00-.186.185v1.887c0 .102.084.185.186.186m5.893 2.715h2.118a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m-2.93 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.083.185.185.185m-2.964 0h2.119a.185.185 0 00.185-.185V9.006a.185.185 0 00-.184-.186h-2.12a.186.186 0 00-.186.186v1.887c0 .102.084.185.186.185m-2.92 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.082.185.185.185M23.763 9.89c-.065-.051-.672-.51-1.954-.51-.338.001-.676.03-1.01.087-.248-1.7-1.653-2.53-1.716-2.566l-.344-.199-.226.327c-.284.438-.49.922-.612 1.43-.23.97-.09 1.882.403 2.661-.595.332-1.55.413-1.744.42H.751a.751.751 0 00-.75.748 11.376 11.376 0 00.692 4.062c.545 1.428 1.355 2.48 2.41 3.124 1.18.723 3.1 1.137 5.275 1.137.983.003 1.963-.086 2.93-.266a12.248 12.248 0 003.823-1.389c.98-.567 1.86-1.288 2.61-2.136 1.252-1.418 1.998-2.997 2.553-4.4h.221c1.372 0 2.215-.549 2.68-1.009.309-.293.55-.65.707-1.046l.098-.288Z"/>
|
||||
{/snippet}
|
||||
|
||||
{#snippet honoIcon(clazz)}
|
||||
<svg class={clazz} role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Hono</title><path d="M12.445.002a45.529 45.529 0 0 0-5.252 8.146 8.595 8.595 0 0 1-.555-.53 27.796 27.796 0 0 0-1.205-1.542 8.762 8.762 0 0 0-1.251 2.12 20.743 20.743 0 0 0-1.448 5.88 8.867 8.867 0 0 0 .338 3.468c1.312 3.48 3.794 5.593 7.445 6.337 3.055.438 5.755-.333 8.097-2.312 2.677-2.59 3.359-5.634 2.047-9.132a33.287 33.287 0 0 0-2.988-5.59A91.34 91.34 0 0 0 12.615.053a.216.216 0 0 0-.17-.051Zm-.336 3.906a50.93 50.93 0 0 1 4.794 6.552c.448.767.817 1.57 1.108 2.41.606 2.386-.044 4.354-1.951 5.904-1.845 1.298-3.87 1.683-6.072 1.156-2.376-.737-3.75-2.335-4.121-4.794a5.107 5.107 0 0 1 .242-2.266c.358-.908.79-1.774 1.3-2.601l1.446-2.121a397.33 397.33 0 0 0 3.254-4.24Z"/></svg>
|
||||
{#snippet honoIcon()}
|
||||
<path fill="currentColor" d="M12.445.002a45.529 45.529 0 0 0-5.252 8.146 8.595 8.595 0 0 1-.555-.53 27.796 27.796 0 0 0-1.205-1.542 8.762 8.762 0 0 0-1.251 2.12 20.743 20.743 0 0 0-1.448 5.88 8.867 8.867 0 0 0 .338 3.468c1.312 3.48 3.794 5.593 7.445 6.337 3.055.438 5.755-.333 8.097-2.312 2.677-2.59 3.359-5.634 2.047-9.132a33.287 33.287 0 0 0-2.988-5.59A91.34 91.34 0 0 0 12.615.053a.216.216 0 0 0-.17-.051Zm-.336 3.906a50.93 50.93 0 0 1 4.794 6.552c.448.767.817 1.57 1.108 2.41.606 2.386-.044 4.354-1.951 5.904-1.845 1.298-3.87 1.683-6.072 1.156-2.376-.737-3.75-2.335-4.121-4.794a5.107 5.107 0 0 1 .242-2.266c.358-.908.79-1.774 1.3-2.601l1.446-2.121a397.33 397.33 0 0 0 3.254-4.24Z"/>
|
||||
{/snippet}
|
||||
|
||||
{#snippet linkedInIcon(clazz)}
|
||||
<svg class={clazz} role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>LinkedIn</title><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
|
||||
{#snippet linkedInIcon()}
|
||||
<path fill="currentColor" d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
|
||||
{/snippet}
|
||||
|
||||
{#snippet xIcon(clazz)}
|
||||
<svg class={clazz} role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>X</title><path d="M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z"/></svg>
|
||||
{#snippet xIcon()}
|
||||
<path fill="currentColor" d="M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z"/>
|
||||
{/snippet}
|
||||
|
||||
{#snippet blueSkyIcon(clazz)}
|
||||
<svg class={clazz} role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Bluesky</title><path d="M12 10.8c-1.087-2.114-4.046-6.053-6.798-7.995C2.566.944 1.561 1.266.902 1.565.139 1.908 0 3.08 0 3.768c0 .69.378 5.65.624 6.479.815 2.736 3.713 3.66 6.383 3.364.136-.02.275-.039.415-.056-.138.022-.276.04-.415.056-3.912.58-7.387 2.005-2.83 7.078 5.013 5.19 6.87-1.113 7.823-4.308.953 3.195 2.05 9.271 7.733 4.308 4.267-4.308 1.172-6.498-2.74-7.078a8.741 8.741 0 0 1-.415-.056c.14.017.279.036.415.056 2.67.297 5.568-.628 6.383-3.364.246-.828.624-5.79.624-6.478 0-.69-.139-1.861-.902-2.206-.659-.298-1.664-.62-4.3 1.24C16.046 4.748 13.087 8.687 12 10.8Z"/></svg>
|
||||
{#snippet blueSkyIcon()}
|
||||
<path fill="currentColor" d="M12 10.8c-1.087-2.114-4.046-6.053-6.798-7.995C2.566.944 1.561 1.266.902 1.565.139 1.908 0 3.08 0 3.768c0 .69.378 5.65.624 6.479.815 2.736 3.713 3.66 6.383 3.364.136-.02.275-.039.415-.056-.138.022-.276.04-.415.056-3.912.58-7.387 2.005-2.83 7.078 5.013 5.19 6.87-1.113 7.823-4.308.953 3.195 2.05 9.271 7.733 4.308 4.267-4.308 1.172-6.498-2.74-7.078a8.741 8.741 0 0 1-.415-.056c.14.017.279.036.415.056 2.67.297 5.568-.628 6.383-3.364.246-.828.624-5.79.624-6.478 0-.69-.139-1.861-.902-2.206-.659-.298-1.664-.62-4.3 1.24C16.046 4.748 13.087 8.687 12 10.8Z"/>
|
||||
{/snippet}
|
||||
|
||||
{#snippet nextDotJsIcon(clazz)}
|
||||
<svg class={clazz} role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Next.js</title><path d="M18.665 21.978C16.758 23.255 14.465 24 12 24 5.377 24 0 18.623 0 12S5.377 0 12 0s12 5.377 12 12c0 3.583-1.574 6.801-4.067 9.001L9.219 7.2H7.2v9.596h1.615V9.251l9.85 12.727Zm-3.332-8.533 1.6 2.061V7.2h-1.6v6.245Z"/></svg>
|
||||
{#snippet nextDotJsIcon()}
|
||||
<path fill="currentColor" d="M18.665 21.978C16.758 23.255 14.465 24 12 24 5.377 24 0 18.623 0 12S5.377 0 12 0s12 5.377 12 12c0 3.583-1.574 6.801-4.067 9.001L9.219 7.2H7.2v9.596h1.615V9.251l9.85 12.727Zm-3.332-8.533 1.6 2.061V7.2h-1.6v6.245Z"/>
|
||||
{/snippet}
|
||||
|
||||
<style lang="postcss">
|
||||
svg {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
}
|
||||
</style>
|
||||
{#snippet lucideIcon(icon)}
|
||||
{@const Icon = icon}
|
||||
<Icon />
|
||||
{/snippet}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { dockerIcon, drizzleIcon, nextDotJsIcon, reactIcon, svelteIcon, typescri
|
|||
import CourseCard from './CourseCard.svelte';
|
||||
import TechListItem from './TechListItem.svelte';
|
||||
import courseData from './course.json';
|
||||
import ExternalLink from '$lib/components/ExternalLink.svelte';
|
||||
|
||||
const courses: Course[] = courseData.courses;
|
||||
</script>
|
||||
|
|
@ -40,47 +41,35 @@ const courses: Course[] = courseData.courses;
|
|||
as:
|
||||
</p>
|
||||
<div class="tech-list">
|
||||
<TechListItem
|
||||
itemText="Svelte"
|
||||
ariaLabel="Svelte"
|
||||
href="https://svelte.dev"
|
||||
clazz="center"
|
||||
icon={svelteIcon}
|
||||
<ExternalLink
|
||||
iconData={{ type: 'svg', icon: svelteIcon, iconClass: 'center' }}
|
||||
linkData={{ href: 'https://svelte.dev', ariaLabel: 'Svelte', title: 'Svelte', target: '_blank', clazz: "tech-list-item", textDecoration: 'none' }}
|
||||
textData={{ text: "Svelte", showIcon: true, location: 'bottom' }}
|
||||
/>
|
||||
<TechListItem
|
||||
itemText="TypeScript"
|
||||
ariaLabel="TypeScript"
|
||||
href="https://www.typescriptlang.org/"
|
||||
clazz="center"
|
||||
icon={typescriptIcon}
|
||||
<ExternalLink
|
||||
iconData={{ type: 'svg', icon: typescriptIcon, iconClass: 'center' }}
|
||||
linkData={{ href: 'https://www.typescriptlang.org/', ariaLabel: 'TypeScript', title: 'TypeScript', target: '_blank', clazz: "tech-list-item", textDecoration: 'none' }}
|
||||
textData={{ text: "TypeScript", showIcon: true, location: 'bottom' }}
|
||||
/>
|
||||
<TechListItem
|
||||
itemText="Drizzle ORM"
|
||||
ariaLabel="Prisma ORM"
|
||||
href="https://orm.drizzle.team/"
|
||||
clazz="center"
|
||||
icon={drizzleIcon}
|
||||
<ExternalLink
|
||||
iconData={{ type: 'svg', icon: drizzleIcon, iconClass: 'center' }}
|
||||
linkData={{ href: 'https://orm.drizzle.team/', ariaLabel: 'Drizzle ORM', title: 'Drizzle ORM', target: '_blank', clazz: "tech-list-item", textDecoration: 'none' }}
|
||||
textData={{ text: "Drizzle ORM", showIcon: true, location: 'bottom' }}
|
||||
/>
|
||||
<TechListItem
|
||||
itemText="React"
|
||||
ariaLabel="React"
|
||||
href="https://reactjs.org/"
|
||||
clazz="center"
|
||||
icon={reactIcon}
|
||||
<ExternalLink
|
||||
iconData={{ type: 'svg', icon: reactIcon, iconClass: 'center' }}
|
||||
linkData={{ href: 'https://reactjs.org/', ariaLabel: 'React', title: 'React', target: '_blank', clazz: "tech-list-item", textDecoration: 'none' }}
|
||||
textData={{ text: "React", showIcon: true, location: 'bottom' }}
|
||||
/>
|
||||
<TechListItem
|
||||
itemText="NextJS"
|
||||
ariaLabel="NextJS"
|
||||
href="https://nextjs.org/"
|
||||
clazz="center"
|
||||
icon={nextDotJsIcon}
|
||||
<ExternalLink
|
||||
iconData={{ type: 'svg', icon: nextDotJsIcon, iconClass: 'center' }}
|
||||
linkData={{ href: 'https://nextjs.org/', ariaLabel: 'Next.js', title: 'Next.js', target: '_blank', clazz: "tech-list-item", textDecoration: 'none' }}
|
||||
textData={{ text: "Next.js", showIcon: true, location: 'bottom' }}
|
||||
/>
|
||||
<TechListItem
|
||||
itemText="Docker"
|
||||
ariaLabel="Docker"
|
||||
href="https://docker.com/"
|
||||
clazz="center"
|
||||
icon={dockerIcon}
|
||||
<ExternalLink
|
||||
iconData={{ type: 'svg', icon: dockerIcon, iconClass: 'center' }}
|
||||
linkData={{ href: 'https://www.docker.com/', ariaLabel: 'Docker', title: 'Docker', target: '_blank', clazz: "tech-list-item", textDecoration: 'none' }}
|
||||
textData={{ text: "Docker", showIcon: true, location: 'bottom' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -155,7 +144,7 @@ const courses: Course[] = courseData.courses;
|
|||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
place-content: center;
|
||||
gap: 1rem;
|
||||
gap: 2rem;
|
||||
margin-top: 1rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,12 +15,9 @@
|
|||
<h3>
|
||||
{#each externalLinks as link}
|
||||
<ExternalLink
|
||||
ariaLabel={link.ariaLabel}
|
||||
href={link.href}
|
||||
showIcon={link.showIcon}
|
||||
>
|
||||
{link.text}
|
||||
</ExternalLink>
|
||||
linkData={{ href: link.href, ariaLabel: link.ariaLabel, title: link.ariaLabel, target: '_blank', clazz: "tech-list-item" }}
|
||||
textData={{ text: link.text, showIcon: link.showIcon, location: 'bottom' }}
|
||||
/>
|
||||
{/each}
|
||||
</h3>
|
||||
<div class="tags">
|
||||
|
|
|
|||
|
|
@ -1,31 +1,28 @@
|
|||
<script lang="ts">
|
||||
import ExternalLink from '$lib/components/ExternalLink.svelte';
|
||||
import { lucideIcon } from '$lib/util/logoIcons.svelte';
|
||||
import type { Snippet } from "svelte";
|
||||
import type { LinkTextType } from '$lib/types/externalLinkTypes';
|
||||
|
||||
interface Props {
|
||||
linkData: LinkTextType;
|
||||
ariaLabel: string;
|
||||
href: string;
|
||||
clazz?: string;
|
||||
itemText: string;
|
||||
icon?: Snippet;
|
||||
children?: any;
|
||||
textData?: LinkTextType;
|
||||
icon: Snippet;
|
||||
}
|
||||
|
||||
let { ariaLabel, href, clazz = '', itemText, icon, children }: Props = $props();
|
||||
let { ariaLabel, href, clazz = '', textData, icon }: Props = $props();
|
||||
</script>
|
||||
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
aria-label={ariaLabel}
|
||||
{href}
|
||||
class={clazz}
|
||||
>
|
||||
{#if icon}
|
||||
{@render icon?.()}
|
||||
{/if}
|
||||
{@render children?.()}
|
||||
<p>{itemText}</p>
|
||||
</a>
|
||||
<ExternalLink
|
||||
ariaLabel={ariaLabel}
|
||||
href={href}
|
||||
linkClass={clazz}
|
||||
icon={lucideIcon}
|
||||
textData={textData}
|
||||
/>
|
||||
|
||||
<style lang="postcss">
|
||||
a {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
// @ts-expect-error: Cannot find module '$lib/content/uses/development.md' or its corresponding type declarations.ts(2307)
|
||||
import MarkShellnutArchitect from "$lib/content/portfolio/professional/mark-shellnut-architect.md";
|
||||
import type { ExternalLinkType } from "$lib/types/externalLinkType";
|
||||
import GitHub from "@iconify-icons/simple-icons/github";
|
||||
import { Tabs } from "bits-ui";
|
||||
import personalSite from "../../lib/assets/images/portfolio/Bradley_Shellnut_New_Site.png?enhanced";
|
||||
import shellnutArchitectWebsite from "../../lib/assets/images/portfolio/Mark_Shellnut_Architect.png?enhanced";
|
||||
|
|
|
|||
|
|
@ -29,6 +29,12 @@
|
|||
--linkHover: var(--shellYellow);
|
||||
--lightHairLine: #c5c5c5;
|
||||
|
||||
--xColor: #000000;
|
||||
--linkedInColor: #0077b5;
|
||||
--githubColor: #000000;
|
||||
--blueskyColor: #0085ff;
|
||||
--emailColor: var(--linkHover);
|
||||
|
||||
/* Styles */
|
||||
--line: solid 1px var(--lineColor);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue