Create index

This commit is contained in:
2025-01-06 19:02:24 +09:00
parent a3335d1583
commit 1db48b4c9a
25 changed files with 2300 additions and 22 deletions

5
apps/web/.env.example Normal file
View File

@@ -0,0 +1,5 @@
# run `yarn gen:env` at the project root to generate
PUBLIC_WEB_DOMAIN="www.cannorin.net"
PUBLIC_WEB_TURNSTILE_SITEKEY="1x00000000000000000000BB"
WEB_TURNSTILE_SECRET_KEY="1x0000000000000000000000000000000AA"

View File

@@ -14,6 +14,7 @@
},
"devDependencies": {
"@sveltejs/adapter-auto": "3.3.1",
"@sveltejs/enhanced-img": "0.4.4",
"@sveltejs/kit": "2.15.1",
"@sveltejs/vite-plugin-svelte": "4.0.4",
"autoprefixer": "10.4.20",
@@ -24,6 +25,12 @@
"vite": "5.4.11"
},
"dependencies": {
"@tailwindcss/typography": "0.5.15"
"@fontsource/poiret-one": "5.1.1",
"@fontsource/zen-kaku-gothic-new": "5.1.1",
"@icons-pack/svelte-simple-icons": "4.0.1",
"@tailwindcss/typography": "0.5.15",
"deepmerge": "4.3.1",
"svelte-seo": "1.6.1",
"tailwind-merge": "2.6.0"
}
}

View File

@@ -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;
}
}

View File

@@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 86 KiB

View 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,
);

View 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
View 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;
}

View File

@@ -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;
}

View 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));
}

View 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>

View File

@@ -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()}

View File

@@ -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>

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,78 @@
import typography from "@tailwindcss/typography";
import type { Config } from "tailwindcss";
import { fontFamily } from "tailwindcss/defaultTheme";
export default {
content: ["./src/**/*.{html,js,svelte,ts}"],
theme: {
extend: {},
container: {
center: true,
padding: "2rem",
},
extend: {
colors: {
border: "rgb(var(--border) / <alpha-value>)",
input: "rgb(var(--input) / <alpha-value>)",
ring: "rgb(var(--ring) / <alpha-value>)",
background: "rgb(var(--background) / <alpha-value>)",
foreground: "rgb(var(--foreground) / <alpha-value>)",
primary: {
DEFAULT: "rgb(var(--primary) / <alpha-value>)",
foreground: "rgb(var(--primary-foreground) / <alpha-value>)",
},
secondary: {
DEFAULT: "rgb(var(--secondary) / <alpha-value>)",
foreground: "rgb(var(--secondary-foreground) / <alpha-value>)",
},
destructive: {
DEFAULT: "rgb(var(--destructive) / <alpha-value>)",
foreground: "rgb(var(--destructive-foreground) / <alpha-value>)",
},
muted: {
DEFAULT: "rgb(var(--muted) / <alpha-value>)",
foreground: "rgb(var(--muted-foreground) / <alpha-value>)",
},
accent: {
DEFAULT: "rgb(var(--accent) / <alpha-value>)",
foreground: "rgb(var(--accent-foreground) / <alpha-value>)",
},
popover: {
DEFAULT: "rgb(var(--popover) / <alpha-value>)",
foreground: "rgb(var(--popover-foreground) / <alpha-value>)",
},
card: {
DEFAULT: "rgb(var(--card) / <alpha-value>)",
foreground: "rgb(var(--card-foreground) / <alpha-value>)",
},
},
fontFamily: {
sans: ["Zen Kaku Gothic New", ...fontFamily.sans],
display: ["Poiret One"],
},
animation: {
blink: "blink 0.6s ease both",
"fade-in": "fade-in 0.3s cubic-bezier(0.390, 0.575, 0.565, 1.000) both",
},
keyframes: {
blink: {
"0%,50%,to": {
opacity: "1",
},
"25%,75%": {
opacity: "0",
},
},
"fade-in": {
"0%": {
opacity: "0",
},
to: {
opacity: "1",
},
},
},
},
},
plugins: [typography],

View File

@@ -1,6 +1,7 @@
import { enhancedImages } from "@sveltejs/enhanced-img";
import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [sveltekit()],
plugins: [enhancedImages(), sveltekit()],
});