Create index
This commit is contained in:
@@ -1,3 +1,48 @@
|
||||
@import "tailwindcss/base";
|
||||
@import "tailwindcss/components";
|
||||
@import "tailwindcss/utilities";
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--foreground: 55 65 81 /* #374151 */;
|
||||
--background: 250 250 250 /* #fafafa */;
|
||||
|
||||
--muted: 174 174 174 /* #aeaeae */;
|
||||
--muted-foreground: 31 31 31 /* #1f1f1f */;
|
||||
|
||||
--popover: 245 248 255;
|
||||
--popover-foreground: 12 0 0;
|
||||
|
||||
--card: 245 248 255;
|
||||
--card-foreground: 12 0 0;
|
||||
|
||||
--border: 55 65 81 /* #374151 */;
|
||||
--input: 55 65 81 /* #374151 */;
|
||||
--ring: 55 65 81 /* #374151 */;
|
||||
|
||||
--primary: 219 96 114 /* DB6072 */;
|
||||
--primary-foreground: 31 31 31 /* #1f1f1f */;
|
||||
|
||||
--secondary: 120 153 212 /* 7899D4 */;
|
||||
--secondary-foreground: 31 31 31 /* #1f1f1f */;
|
||||
|
||||
--accent: 120 153 212 /* 7899D4 */;
|
||||
--accent-foreground: 31 31 31 /* #1f1f1f */;
|
||||
|
||||
--destructive: 219 96 114 /* DB6072 */;
|
||||
--destructive-foreground: 31 31 31 /* #1f1f1f */;
|
||||
|
||||
color-scheme: only light;
|
||||
scrollbar-gutter: stable;
|
||||
padding-left: calc(100vw - 100%);
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground min-h-screen flex flex-col justify-normal;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
<html lang="ja">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
BIN
apps/web/src/assets/images/static/icon/jakeko.webp
Normal file
BIN
apps/web/src/assets/images/static/icon/jakeko.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 87 KiB |
BIN
apps/web/src/assets/images/static/icon/logic-chang.webp
Normal file
BIN
apps/web/src/assets/images/static/icon/logic-chang.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
BIN
apps/web/src/assets/images/static/icon/tapl.webp
Normal file
BIN
apps/web/src/assets/images/static/icon/tapl.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
1
apps/web/src/assets/images/static/logic-chang.svg
Normal file
1
apps/web/src/assets/images/static/logic-chang.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 86 KiB |
38
apps/web/src/lib/common/seo.ts
Normal file
38
apps/web/src/lib/common/seo.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { ComponentProps } from "svelte";
|
||||
import type SvelteSeo from "svelte-seo";
|
||||
|
||||
import deepmerge from "deepmerge";
|
||||
|
||||
export type Seo = ComponentProps<SvelteSeo>;
|
||||
|
||||
export const defaultSeo: Seo = {
|
||||
title: "cannorin.net",
|
||||
description: "cannorin's website",
|
||||
themeColor: "#fafafa",
|
||||
|
||||
openGraph: {
|
||||
title: "cannorin.net",
|
||||
type: "website",
|
||||
description: "cannorin's website",
|
||||
locale: "ja_JP",
|
||||
},
|
||||
|
||||
twitter: {
|
||||
title: "cannorin.net",
|
||||
description: "cannorin's website",
|
||||
card: "summary",
|
||||
creator: "cannorin3",
|
||||
},
|
||||
};
|
||||
|
||||
export const mergeSeo = (
|
||||
target: Seo,
|
||||
...sources: (Seo | undefined | false)[]
|
||||
) =>
|
||||
sources.reduce<Seo>(
|
||||
(acc, current) =>
|
||||
deepmerge(acc, current || {}, {
|
||||
arrayMerge: (_target, source) => source,
|
||||
}),
|
||||
target,
|
||||
);
|
||||
2
apps/web/src/lib/constants.ts
Normal file
2
apps/web/src/lib/constants.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const limitWidth =
|
||||
"mx-auto w-full lg:max-w-[904px] xl:max-w-[1264px] px-6 lg:px-8";
|
||||
45
apps/web/src/lib/global.d.ts
vendored
Normal file
45
apps/web/src/lib/global.d.ts
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
interface String {
|
||||
concat<S extends string>(string: S): `${this}${S}`;
|
||||
concat<S1 extends string, S2 extends string>(
|
||||
s1: S1,
|
||||
s2: S2,
|
||||
): `${this}${S1}${S2}`;
|
||||
startsWith<S extends string>(searchString: S): this is `${S}${string}`;
|
||||
endsWith<S extends string>(searchString: S): this is `${string}${S}`;
|
||||
includes<S extends string>(
|
||||
searchString: S,
|
||||
position?: number,
|
||||
): this is `${string}${S}${string}`;
|
||||
}
|
||||
|
||||
type LiteralUnionLike<T> = T extends string
|
||||
? T extends ""
|
||||
? T
|
||||
: T extends `${T}${T}`
|
||||
? never
|
||||
: T
|
||||
: T extends number
|
||||
? `${T}0` extends `${number}`
|
||||
? T
|
||||
: never
|
||||
: T extends null | undefined
|
||||
? T
|
||||
: never;
|
||||
|
||||
interface Array<T> {
|
||||
includes(
|
||||
searchElement: T extends LiteralUnionLike<T> ? unknown : never,
|
||||
fromIndex?: number,
|
||||
): searchElement is T extends LiteralUnionLike<T> ? T : never;
|
||||
}
|
||||
|
||||
interface ReadonlyArray<T> {
|
||||
includes(
|
||||
searchElement: T extends LiteralUnionLike<T> ? unknown : never,
|
||||
fromIndex?: number,
|
||||
): searchElement is T extends LiteralUnionLike<T> ? T : never;
|
||||
}
|
||||
|
||||
interface Map<K> {
|
||||
has(key: Weaken<K>): key is K;
|
||||
}
|
||||
@@ -1 +1,7 @@
|
||||
// place files you want to import through the `$lib` alias in this folder.
|
||||
export function tryOneOf<const T>(
|
||||
value: T extends LiteralUnionLike<T> ? unknown : never,
|
||||
consts: readonly T[],
|
||||
) {
|
||||
if (consts.includes(value)) return value;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
6
apps/web/src/lib/utils.ts
Normal file
6
apps/web/src/lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { type ClassValue, clsx } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
59
apps/web/src/routes/+error.svelte
Normal file
59
apps/web/src/routes/+error.svelte
Normal file
@@ -0,0 +1,59 @@
|
||||
<script lang="ts">
|
||||
import { page } from "$app/state";
|
||||
import { limitWidth } from "$lib/constants";
|
||||
import { cn } from "$lib/utils";
|
||||
|
||||
const codeNames: Record<number, string> = {
|
||||
400: "Bad Request",
|
||||
401: "Unauthorized",
|
||||
402: "Payment Required",
|
||||
403: "Forbidden",
|
||||
404: "Not Found",
|
||||
405: "Method Not Allowed",
|
||||
406: "Not Acceptable",
|
||||
407: "Proxy Authentication Required",
|
||||
408: "Request Timeout",
|
||||
409: "Conflict",
|
||||
410: "Gone",
|
||||
411: "Length Required",
|
||||
412: "Precondition Required",
|
||||
413: "Payload Too Large",
|
||||
414: "URI Too Long",
|
||||
415: "Unsupported Media Type",
|
||||
416: "Range Not Satisfiable",
|
||||
417: "Expectation Failed",
|
||||
429: "Too Many Requests",
|
||||
451: "Unavailable For Legal Reasons",
|
||||
500: "Internal Server Error",
|
||||
501: "Not Implemented",
|
||||
502: "Bad Gateway",
|
||||
503: "Service Unavailable",
|
||||
504: "Gateway Timeout",
|
||||
505: "HTTP Version Not Supported",
|
||||
};
|
||||
</script>
|
||||
|
||||
<main class={cn("flex grow flex-col items-center justify-center gap-10 py-10 relative overflow-hidden", limitWidth)}>
|
||||
<section class="flex min-w-full flex-col items-center gap-6 ">
|
||||
<h1 class="text-center text-5xl font-display leading-[1.125]">
|
||||
<span>{page.status}</span>
|
||||
<span class="whitespace-nowrap">
|
||||
{codeNames[page.status] ?? "Unknown Error"}
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<div class="text-sm">
|
||||
{#if page.status === 400}
|
||||
<p>リクエストの結果エラーが発生しました。</p>
|
||||
{:else if page.status === 401}
|
||||
<p>このページを表示するにはログインする必要があります。</p>
|
||||
{:else if page.status === 403}
|
||||
<p>このページを閲覧する権限がありません。</p>
|
||||
{:else if page.status === 404}
|
||||
<p>お探しのページは見つかりませんでした。</p>
|
||||
{:else}
|
||||
<p>{page.error?.message}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
@@ -1,6 +1,23 @@
|
||||
<script lang="ts">
|
||||
import "../app.css";
|
||||
import "./webfont.css";
|
||||
|
||||
import { page } from "$app/state";
|
||||
import { PUBLIC_WEB_DOMAIN } from "$env/static/public";
|
||||
import { defaultSeo, mergeSeo } from "$lib/common/seo";
|
||||
import SvelteSeo from "svelte-seo";
|
||||
|
||||
let { children } = $props();
|
||||
let seo = $derived.by(() =>
|
||||
mergeSeo(defaultSeo, page.data.seo, {
|
||||
canonical: `https://${PUBLIC_WEB_DOMAIN}${page.url.pathname}`,
|
||||
openGraph: {
|
||||
url: `https://${PUBLIC_WEB_DOMAIN}${page.url.pathname}`,
|
||||
},
|
||||
}),
|
||||
);
|
||||
</script>
|
||||
|
||||
<SvelteSeo {...seo} />
|
||||
|
||||
{@render children()}
|
||||
|
||||
@@ -1,2 +1,255 @@
|
||||
<h1>Welcome to SvelteKit</h1>
|
||||
<p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p>
|
||||
<script lang="ts">
|
||||
import { limitWidth } from "$lib/constants";
|
||||
import { cn } from "$lib/utils";
|
||||
|
||||
import SiBandcamp from "@icons-pack/svelte-simple-icons/icons/SiBandcamp";
|
||||
import SiDiscord from "@icons-pack/svelte-simple-icons/icons/SiDiscord";
|
||||
import SiGithub from "@icons-pack/svelte-simple-icons/icons/SiGithub";
|
||||
import SiKeybase from "@icons-pack/svelte-simple-icons/icons/SiKeybase";
|
||||
import SiMisskey from "@icons-pack/svelte-simple-icons/icons/SiMisskey";
|
||||
import SiMixcloud from "@icons-pack/svelte-simple-icons/icons/SiMixcloud";
|
||||
import SiOrcid from "@icons-pack/svelte-simple-icons/icons/SiOrcid";
|
||||
import SiQiita from "@icons-pack/svelte-simple-icons/icons/SiQiita";
|
||||
import SiResearchgate from "@icons-pack/svelte-simple-icons/icons/SiResearchgate";
|
||||
import SiSoundcloud from "@icons-pack/svelte-simple-icons/icons/SiSoundcloud";
|
||||
import SiSteam from "@icons-pack/svelte-simple-icons/icons/SiSteam";
|
||||
import SiTwitch from "@icons-pack/svelte-simple-icons/icons/SiTwitch";
|
||||
import SiX from "@icons-pack/svelte-simple-icons/icons/SiX";
|
||||
import SiZenn from "@icons-pack/svelte-simple-icons/icons/SiZenn";
|
||||
</script>
|
||||
|
||||
<!-- Sections -->
|
||||
<main class={cn(limitWidth, "flex grow flex-col items-center gap-12 lg:gap-16 py-8")}>
|
||||
<h1 class="font-display text-4xl md:text-5xl lg:text-6xl animate-fade-in">cannorin.net</h1>
|
||||
|
||||
<section class="grid grid-cols-1 md:grid-cols-3 gap-4 lg:gap-8">
|
||||
<h2 class="sr-only">自己紹介</h2>
|
||||
|
||||
<section class="card">
|
||||
<enhanced:img src="../assets/images/static/icon/jakeko.webp?w=1080,800,600,400,300" sizes="min(100vw, 400px)" alt="" />
|
||||
<div class="body">
|
||||
<h3>DJ / Composer</h3>
|
||||
<p>ワイヤードでは VRChat を拠点に、リアルワールドでは大阪を拠点にして、DJ や作曲などの音楽活動を行っています。</p>
|
||||
<nav>
|
||||
<ul class="links">
|
||||
<li>
|
||||
<a href="https://cannorin.bandcamp.com/" target="_blank">
|
||||
<SiBandcamp title="Bandcamp" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://soundcloud.com/cannorin" target="_blank">
|
||||
<SiSoundcloud title="Soundcloud" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://mixcloud.com/cannorin" target="_blank">
|
||||
<SiMixcloud title="Mixcloud" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="more">
|
||||
<button disabled class="tooltip cursor-not-allowed">
|
||||
<span class="tooltip-text">coming soon™</span>
|
||||
More
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<enhanced:img src="../assets/images/static/icon/tapl.webp?w=1080,800,640,400,320" sizes="min(100vw, 400px)" alt="" />
|
||||
<div class="body">
|
||||
<h3>Developer</h3>
|
||||
<p>フルスタックエンジニアとして働いており、いくつかの OSS に関わっています。また、趣味で Web サービスを運用しています。</p>
|
||||
<nav>
|
||||
<ul class="links w-full">
|
||||
<li>
|
||||
<a href="https://github.com/cannorin" target="_blank">
|
||||
<SiGithub title="GitHub" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://qiita.com/cannorin" target="_blank">
|
||||
<SiQiita title="Qiita" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://zenn.dev/cannorin" target="_blank">
|
||||
<SiZenn title="Zenn" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="more">
|
||||
<button disabled class="tooltip cursor-not-allowed">
|
||||
<span class="tooltip-text">coming soon™</span>
|
||||
More
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<enhanced:img src="../assets/images/static/icon/logic-chang.webp?w=1080,800,600,400,300" sizes="min(100vw, 400px)" alt="" />
|
||||
<div class="body">
|
||||
<h3>Graduate Student</h3>
|
||||
<p>大学院において数理論理学を研究しており、非古典論理、特に様相論理を専門としています。2025年度より博士課程に進学します。</p>
|
||||
<nav>
|
||||
<ul class="links">
|
||||
<li>
|
||||
<a href="https://orcid.org/0009-0009-3946-4260" target="_blank">
|
||||
<SiOrcid title="ORCiD" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.researchgate.net/profile/Yuta-Sato-22" target="_blank">
|
||||
<SiResearchgate title="ResearchGate" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="more">
|
||||
<button disabled class="tooltip cursor-not-allowed">
|
||||
<span class="tooltip-text">coming soon™</span>
|
||||
More
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section class="followme">
|
||||
<h2 class="font-display text-2xl">Follow me on:</h2>
|
||||
<nav>
|
||||
<ul class="flex flex-row flex-nowrap items-center gap-4 text-sm">
|
||||
<li>
|
||||
<a href="https://keybase.io/cannorin" target="_blank">
|
||||
<SiKeybase title="Keybase" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://x.com/cannorin3" target="_blank">
|
||||
<SiX title="X (Twitter)" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://misskey.cannorin.net/@cannorin" target="_blank">
|
||||
<SiMisskey title="Misskey" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.twitch.tv/cannorin" target="_blank">
|
||||
<SiTwitch title="Twitch" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://discord.com/users/497190979216867329" target="_blank">
|
||||
<SiDiscord title="Discord" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://steamcommunity.com/id/cannorin/" target="_blank">
|
||||
<SiSteam title="Steam" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<style lang="postcss">
|
||||
.card {
|
||||
@apply relative max-w-[400px] rounded-2xl flex flex-col items-center shadow animate-fade-in overflow-clip;
|
||||
|
||||
&::before {
|
||||
@apply absolute top-0 left-0 right-0 bottom-0 rounded-2xl border border-transparent;
|
||||
z-index: -1;
|
||||
content: "";
|
||||
background: linear-gradient(transparent, rgb(var(--foreground) / 10%), rgb(var(--foreground) / 20%), rgb(var(--foreground) / 100%)) border-box border-box;
|
||||
mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0) border-box;
|
||||
mask-composite: exclude;
|
||||
}
|
||||
|
||||
picture {
|
||||
@apply w-full;
|
||||
mask: linear-gradient(rgb(0 0 0 / 100%), rgb(0 0 0 / 90%), rgb(0 0 0 / 80%), transparent);
|
||||
|
||||
img {
|
||||
@apply aspect-video md:aspect-square object-cover;
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
@apply h-full p-5 flex flex-col gap-4;
|
||||
|
||||
h3 {
|
||||
@apply text-2xl font-display;
|
||||
}
|
||||
|
||||
> :last-child {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.links {
|
||||
@apply flex flex-wrap items-center gap-4 text-sm;
|
||||
.more {
|
||||
@apply flex items-center ml-auto rounded-full;
|
||||
background: linear-gradient(45deg, rgb(var(--primary) / 50%), rgb(var(--secondary) / 50%));
|
||||
> * {
|
||||
@apply px-3 py-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.followme {
|
||||
@apply flex flex-row flex-wrap items-center justify-center gap-4 px-10 py-4 animate-fade-in rounded-full;
|
||||
background: linear-gradient(45deg, rgb(var(--primary) / 50%), rgb(var(--secondary) / 50%));
|
||||
}
|
||||
|
||||
/* カーソルを重ねる要素 */
|
||||
.tooltip {
|
||||
position: relative; /* ツールチップの位置の基準に */
|
||||
}
|
||||
|
||||
/* ツールチップのテキスト */
|
||||
.tooltip-text {
|
||||
opacity: 0; /* はじめは隠しておく */
|
||||
visibility: hidden; /* はじめは隠しておく */
|
||||
position: absolute; /* 絶対配置 */
|
||||
left: 50%; /* 親に対して中央配置 */
|
||||
transform: translateX(-50%); /* 親に対して中央配置 */
|
||||
bottom: 34px; /* 親要素下からの位置 */
|
||||
display: inline-block;
|
||||
padding: 5px; /* 余白 */
|
||||
white-space: nowrap; /* テキストを折り返さない */
|
||||
font-size: 0.8rem; /* フォントサイズ */
|
||||
line-height: 1.3; /* 行間 */
|
||||
background: rgb(var(--foreground) / 1); /* 背景色 */
|
||||
color: rgb(var(--background) / 1); /* 文字色 */
|
||||
border-radius: 8px; /* 角丸 */
|
||||
transition: 0.3s ease-in; /* アニメーション */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 25px;
|
||||
left: 50%;
|
||||
margin-left: -7px;
|
||||
border: 7px solid transparent;
|
||||
border-top: 7px solid rgb(var(--foreground) / 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* ホバー時にツールチップの非表示を解除 */
|
||||
.tooltip:hover .tooltip-text {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
</style>
|
||||
|
||||
1237
apps/web/src/routes/webfont.css
Normal file
1237
apps/web/src/routes/webfont.css
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user