-
VimConf 2025
+
-
+ const menuLinks = mobileMenu?.querySelectorAll("a");
+ menuLinks?.forEach((link) => {
+ link.addEventListener("click", () => {
+ toggleMenu();
+ });
+ });
+
diff --git a/2025/src/components/KeynoteSpeakers.astro b/2025/src/components/KeynoteSpeakers.astro
new file mode 100644
index 00000000..7bb8b5d3
--- /dev/null
+++ b/2025/src/components/KeynoteSpeakers.astro
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+ 基調講演者は近日公開
+
+
+ 基調講演者は近日公開予定です。お楽しみに!
+
+
+
+
diff --git a/2025/src/components/LanguagePicker.astro b/2025/src/components/LanguagePicker.astro
index ce105c02..453fbe43 100644
--- a/2025/src/components/LanguagePicker.astro
+++ b/2025/src/components/LanguagePicker.astro
@@ -1,51 +1,56 @@
---
-import { LANGUAGES } from '@/i18n';
+import { LANGUAGES } from "@/i18n";
// @ts-expect-error not found
-import { base } from 'astro:config/client';
-
-const pathname = Astro.url.pathname.replace(import.meta.env.BASE_URL, '');
-const currentLang = pathname.split('/')[1];
+import { base } from "astro:config/client";
+const pathname = Astro.url.pathname.replace(import.meta.env.BASE_URL, "");
+const currentLang = pathname.split("/")[1];
---
+
diff --git a/2025/src/components/OgImage.tsx b/2025/src/components/OgImage.tsx
index 88439227..0e18b588 100644
--- a/2025/src/components/OgImage.tsx
+++ b/2025/src/components/OgImage.tsx
@@ -1,41 +1,39 @@
-import { Buffer } from 'node:buffer';
-import LogoWhite from '@/assets/logo-white.svg?raw';
-import { MAIN_BACKGROUND_COLOR } from '@/consts';
-import satori from 'satori';
+import { Buffer } from "node:buffer";
+import LogoWhite from "@/assets/logo-white.svg?raw";
+import { MAIN_BACKGROUND_COLOR } from "@/consts";
+import satori from "satori";
-import sharp from 'sharp';
+import sharp from "sharp";
/* convert string to base64 */
-const LogoWhiteUrl = `data:image/svg+xml;base64,${Buffer.from(LogoWhite).toString('base64')}`;
+const LogoWhiteUrl = `data:image/svg+xml;base64,${Buffer.from(LogoWhite).toString("base64")}`;
export async function ogImage() {
- const svg = await satori(
- (
-
-
-
- ),
- {
- width: 1600,
- height: 900,
- fonts: [],
- },
- );
+ const svg = await satori(
+
+
+
,
+ {
+ width: 1600,
+ height: 900,
+ fonts: [],
+ },
+ );
- return sharp(Buffer.from(svg)).png().toBuffer();
+ return sharp(Buffer.from(svg)).png().toBuffer();
}
diff --git a/2025/src/components/Schedule.astro b/2025/src/components/Schedule.astro
new file mode 100644
index 00000000..24d1baf0
--- /dev/null
+++ b/2025/src/components/Schedule.astro
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+ タイムテーブルは近日公開
+
+
+ カンファレンスの詳細なスケジュールは近日公開されます
+
+
+
+
diff --git a/2025/src/components/Sponsors/index.astro b/2025/src/components/Sponsors/index.astro
new file mode 100644
index 00000000..9693bb2f
--- /dev/null
+++ b/2025/src/components/Sponsors/index.astro
@@ -0,0 +1,256 @@
+
diff --git a/2025/src/components/Staff/StaffCard.astro b/2025/src/components/Staff/StaffCard.astro
new file mode 100644
index 00000000..282d14ee
--- /dev/null
+++ b/2025/src/components/Staff/StaffCard.astro
@@ -0,0 +1,88 @@
+---
+import GithubIcon from "@/assets/github-icon.png?url";
+import XIcon from "@/assets/x-icon.png?url";
+import BlueSkyIcon from "@/assets/bluesky.jpg?url";
+type Props = {
+ name: string;
+ alsoKnownAs?: string;
+ image: string;
+ github: string;
+ x?: string;
+ bluesky?: string;
+};
+
+const { name, alsoKnownAs, image, github, x, bluesky }: Props = Astro.props;
+---
+
+
+
+
+
+
+
+
+
+ {name}
+
+
+ {
+ alsoKnownAs && (
+
+ あるいは{alsoKnownAs}
+
+ )
+ }
+
+
+
+
+
diff --git a/2025/src/components/Staff/index.astro b/2025/src/components/Staff/index.astro
new file mode 100644
index 00000000..7c3d349d
--- /dev/null
+++ b/2025/src/components/Staff/index.astro
@@ -0,0 +1,64 @@
+---
+import StaffCard from "./StaffCard.astro";
+---
+
+
diff --git a/2025/src/components/Top.astro b/2025/src/components/Top.astro
new file mode 100644
index 00000000..6c2ba406
--- /dev/null
+++ b/2025/src/components/Top.astro
@@ -0,0 +1,49 @@
+---
+import VimLogo from "./VimLogo.astro";
+---
+
+
+
+
+
+
+
+
+ VimConf 2025 Small
+
+
+
+
+
diff --git a/2025/src/components/Venue.astro b/2025/src/components/Venue.astro
new file mode 100644
index 00000000..faf40bc3
--- /dev/null
+++ b/2025/src/components/Venue.astro
@@ -0,0 +1,85 @@
+---
+import VenueMap from "@/components/VenueMap.astro";
+---
+
+
+
+
+
+
+
+
+ アキバプラザ・アキバホール
+
+
+
+
+
+
+
+
東京都千代田区神田練塀町3
+
+
+
+
+
+
+
+
+
2025年11月2日(日)
+
+
+
+
+
+
+
+
+
+
+
diff --git a/2025/src/components/VenueMap.astro b/2025/src/components/VenueMap.astro
index 61466555..b75015e7 100644
--- a/2025/src/components/VenueMap.astro
+++ b/2025/src/components/VenueMap.astro
@@ -1,15 +1,13 @@
---
-const src = 'https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3240.053340275775!2d139.77188631510504!3d35.70030498018984!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x60188ea7a29cf80b%3A0xdce74a4dd51e0a66!2z5a-M5aOr44K944OV44OI44Ki44Kt44OQ44OX44Op44K2!5e0!3m2!1sja!2sjp!4v1513741028586&key=AIzaSyDYecdzqzXTkep-BSPK-rl52Yx3D1HFjME';
+const src =
+ "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3240.053340275775!2d139.77188631510504!3d35.70030498018984!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x60188ea7a29cf80b%3A0xdce74a4dd51e0a66!2z5a-M5aOr44K944OV44OI44Ki44Kt44OQ44OX44Op44K2!5e0!3m2!1sja!2sjp!4v1513741028586&key=AIzaSyDYecdzqzXTkep-BSPK-rl52Yx3D1HFjME";
---
-
+
+
diff --git a/2025/src/components/VimLogo.astro b/2025/src/components/VimLogo.astro
new file mode 100644
index 00000000..6cfe3893
--- /dev/null
+++ b/2025/src/components/VimLogo.astro
@@ -0,0 +1,8 @@
+---
+import VimLogoRaw from "@/assets/Vimlogo.svg?raw";
+const VimLogoUrl = `data:image/svg+xml;base64,${Buffer.from(VimLogoRaw).toString("base64")}`;
+
+const { class: className } = Astro.props;
+---
+
+
diff --git a/2025/src/consts/_domain.ts b/2025/src/consts/_domain.ts
index 450dd53e..6c7bfa9e 100644
--- a/2025/src/consts/_domain.ts
+++ b/2025/src/consts/_domain.ts
@@ -1,6 +1,8 @@
-import fs from 'node:fs';
-import path from 'node:path';
+import fs from "node:fs";
+import path from "node:path";
-const CNAME = fs.readFileSync(path.join(import.meta.dirname, '../../../', 'CNAME'), 'utf-8').trim();
+const CNAME = fs
+ .readFileSync(path.join(import.meta.dirname, "../../../", "CNAME"), "utf-8")
+ .trim();
export const DOMAIN = `https://${CNAME}`;
diff --git a/2025/src/consts/index.ts b/2025/src/consts/index.ts
index 33b6cd5d..b86e151d 100644
--- a/2025/src/consts/index.ts
+++ b/2025/src/consts/index.ts
@@ -1,8 +1,8 @@
// @ts-expect-error not found
-import { base } from 'astro:config/client';
-import * as ufo from 'ufo';
+import { base } from "astro:config/client";
+import * as ufo from "ufo";
-import { DOMAIN } from './_domain.js' with { type: 'macro' };
+import { DOMAIN } from "./_domain.js" with { type: "macro" };
// eslint-disable-next-line ts/no-unsafe-argument
export const BASE_URL = ufo.joinURL(DOMAIN, base);
diff --git a/2025/src/i18n/index.ts b/2025/src/i18n/index.ts
index 4d9ab988..c7debb6b 100644
--- a/2025/src/i18n/index.ts
+++ b/2025/src/i18n/index.ts
@@ -1,7 +1,9 @@
-export const LANGUAGES = ['ja', 'en'] as const;
+export const LANGUAGES = ["ja", "en"] as const;
-type Lang = typeof LANGUAGES[number];
+type Lang = (typeof LANGUAGES)[number];
-export function useTranslate(lang: Lang): (...translations: string[]) => string {
- return (...translations: string[]) => translations[LANGUAGES.indexOf(lang)]; ;
+export function useTranslate(
+ lang: Lang,
+): (...translations: string[]) => string {
+ return (...translations: string[]) => translations[LANGUAGES.indexOf(lang)];
}
diff --git a/2025/src/layouts/Layout.astro b/2025/src/layouts/Layout.astro
index 49e039f7..2e5f356f 100644
--- a/2025/src/layouts/Layout.astro
+++ b/2025/src/layouts/Layout.astro
@@ -1,70 +1,72 @@
---
-import favicon from '@/assets/favicon.ico?url';
-import Footer from '@/components/Footer.astro';
-import Header from '@/components/Header.astro';
-import { BASE_URL } from '@/consts';
-import { GoogleFontsOptimizer } from 'astro-google-fonts-optimizer';
-import { SEO } from 'astro-seo';
+import favicon from "@/assets/favicon.ico?url";
+import Footer from "@/components/Footer.astro";
+import Header from "@/components/Header.astro";
+import { BASE_URL } from "@/consts";
+import { GoogleFontsOptimizer } from "astro-google-fonts-optimizer";
+import { SEO } from "astro-seo";
-import * as ufo from 'ufo';
+import * as ufo from "ufo";
-const { lang = 'ja' } = Astro.props;
+import "../styles/global.css";
-const title = 'VimConf 2025';
-const description = 'VimConf 2025';
-const ogImageUrl = ufo.joinURL(BASE_URL, 'og.png');
+const { lang = "ja" } = Astro.props;
+
+const title = "VimConf 2025";
+const description = "VimConf 2025";
+const ogImageUrl = ufo.joinURL(BASE_URL, "og.png");
---
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/2025/src/pages/[lang]/index.astro b/2025/src/pages/[lang]/index.astro
index 0509695f..951a3e4d 100644
--- a/2025/src/pages/[lang]/index.astro
+++ b/2025/src/pages/[lang]/index.astro
@@ -1,247 +1,27 @@
---
-import AnnouncementSlides from '@/components/AnnouncementSlides.astro';
-import VenueMap from '@/components/VenueMap.astro';
-import { MAIN_BACKGROUND_COLOR } from '@/consts';
-import { LANGUAGES, useTranslate } from '@/i18n';
+import AboutVimConf from "../../components/AboutVimConf.astro";
+import KeynoteSpeakers from "../../components/KeynoteSpeakers.astro";
+import Schedule from "../../components/Schedule.astro";
+import Staff from "../../components/Staff/index.astro";
+import Top from "../../components/Top.astro";
+import Venue from "../../components/Venue.astro";
+import "../../styles/global.css";
+import { LANGUAGES } from "@/i18n";
-import Layout from '@/layouts/Layout.astro';
+import Layout from "@/layouts/Layout.astro";
export function getStaticPaths() {
- return LANGUAGES.map(lang => ({ params: { lang } }));
+ return LANGUAGES.map((lang) => ({ params: { lang } }));
}
-
-const { lang } = Astro.params;
-const t = useTranslate(lang);
---
-
-
-
-
- {t(
- 'VimConf は、世界初かつ世界で唯一のコミュニティによって定期運営されているVimの国際カンファレンスです。',
- `VimConf is the world's first and only regularly organized international Vim conference run by the community.`,
- )}
-
-
{t('概要', 'Event Details')}
-
-
- {t('VimConf 2025の概要', 'Details Of VimConf 2025')}
-
-
-
- {t('開催日(予定)', 'Date(Planned)')}
-
- {t('2025年11月2日(日)', 'November 2, 2025 (Sunday)')}
-
-
- {t('時間', 'Time')}
- {t(
- '9:30 - 17:30 (懇親会: 17:30 - 19:30)',
- '9:30 AM - 5:30 PM JST (Networking Party: 5:30 PM - 7:30 PM JST)',
- )}
-
-
- {t('会場', 'Venue')}
-
-
- {t(
- 'アキバプラザ・アキバホール',
- 'Akiba Plaza - Akiba Hall',
- )}
-
- {/* XXX: Workaround for https://github.com/withastro/compiler/issues/958 */}
-
-
-
-
-
{t('その他', 'Additional Information')}
-
{t(
- '発表スライドはすべて英語です。発表は英語または日本語のいずれかの言語で行われます。',
- 'All presentation slides will be in English. Presentations will be given in either English or Japanese.',
- )}
-
{t(
- '登壇の様子を撮影し、後日 YouTube にアップロードします。',
- 'The presentations will be recorded and uploaded to YouTube after the event.',
- )}
-
{t(
- '会場内ではカメラマンによる写真撮影が行われますのでご了承ください。',
- 'Please note that professional photographers will be taking pictures during the event.',
- )}
-
{t(
- '写真もまとめてWeb上に掲載されます。',
- 'Photos will be published online after the event.',
- )}
-
-
-
-
-
-
-
- {t(
- 'VimConf 2025は、普段なかなか顔を合わせられないVimmer同士が情熱を交わし、知見を共有するための貴重な場です。\n今年は、イベント継続の担保と柔軟な運営のため、必要な資金をスポンサー様のご支援で集める仕組みを採用します。',
- 'VimConf 2025 is a valuable opportunity for Vimmers, who rarely get to meet in person, to share their passion and knowledge.\nThis year, to ensure the event\'s sustainability and flexible operation, we are seeking financial support through sponsorships.',
- )}
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
diff --git a/2025/src/pages/index.astro b/2025/src/pages/index.astro
index 8184651c..ea46bd93 100644
--- a/2025/src/pages/index.astro
+++ b/2025/src/pages/index.astro
@@ -1,6 +1,7 @@
---
-import Layout from '@/layouts/Layout.astro';
+import Layout from "@/layouts/Layout.astro";
---
+
diff --git a/2025/src/pages/og.png.ts b/2025/src/pages/og.png.ts
index b422eca0..c07695d1 100644
--- a/2025/src/pages/og.png.ts
+++ b/2025/src/pages/og.png.ts
@@ -1,11 +1,11 @@
-import { ogImage } from '@/components/OgImage';
+import { ogImage } from "@/components/OgImage";
export async function GET() {
- const body = await ogImage();
+ const body = await ogImage();
- return new Response(body, {
- headers: {
- 'content-type': 'image/png',
- },
- });
+ return new Response(body, {
+ headers: {
+ "content-type": "image/png",
+ },
+ });
}
diff --git a/2025/src/styles/global.css b/2025/src/styles/global.css
new file mode 100644
index 00000000..c78fde6e
--- /dev/null
+++ b/2025/src/styles/global.css
@@ -0,0 +1,110 @@
+@import "tailwindcss";
+@config "../../tailwind.config.ts";
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+body {
+ font-family: Arial, Helvetica, sans-serif;
+}
+
+@layer utilities {
+ .text-balance {
+ text-wrap: balance;
+ }
+ .border-border {
+ border-color: hsl(var(--border));
+ }
+}
+
+@utility bg-background {
+ background-color: hsl(var(--background));
+}
+
+@utility border-border {
+ border-color: hsl(var(--border));
+}
+@utility text-foreground {
+ color: hsl(var(--foreground));
+}
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 0 0% 3.9%;
+ --card: 0 0% 100%;
+ --card-foreground: 0 0% 3.9%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 0 0% 3.9%;
+ --primary: 0 0% 9%;
+ --primary-foreground: 0 0% 98%;
+ --secondary: 0 0% 96.1%;
+ --secondary-foreground: 0 0% 9%;
+ --muted: 0 0% 96.1%;
+ --muted-foreground: 0 0% 45.1%;
+ --accent: 0 0% 96.1%;
+ --accent-foreground: 0 0% 9%;
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 0 0% 98%;
+ --border: 0 0% 89.8%;
+ --input: 0 0% 89.8%;
+ --ring: 0 0% 3.9%;
+ --chart-1: 12 76% 61%;
+ --chart-2: 173 58% 39%;
+ --chart-3: 197 37% 24%;
+ --chart-4: 43 74% 66%;
+ --chart-5: 27 87% 67%;
+ --radius: 0.5rem;
+ --sidebar-background: 0 0% 98%;
+ --sidebar-foreground: 240 5.3% 26.1%;
+ --sidebar-primary: 240 5.9% 10%;
+ --sidebar-primary-foreground: 0 0% 98%;
+ --sidebar-accent: 240 4.8% 95.9%;
+ --sidebar-accent-foreground: 240 5.9% 10%;
+ --sidebar-border: 220 13% 91%;
+ --sidebar-ring: 217.2 91.2% 59.8%;
+ }
+ .dark {
+ --background: 0 0% 3.9%;
+ --foreground: 0 0% 98%;
+ --card: 0 0% 3.9%;
+ --card-foreground: 0 0% 98%;
+ --popover: 0 0% 3.9%;
+ --popover-foreground: 0 0% 98%;
+ --primary: 0 0% 98%;
+ --primary-foreground: 0 0% 9%;
+ --secondary: 0 0% 14.9%;
+ --secondary-foreground: 0 0% 98%;
+ --muted: 0 0% 14.9%;
+ --muted-foreground: 0 0% 63.9%;
+ --accent: 0 0% 14.9%;
+ --accent-foreground: 0 0% 98%;
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 0 0% 98%;
+ --border: 0 0% 14.9%;
+ --input: 0 0% 14.9%;
+ --ring: 0 0% 83.1%;
+ --chart-1: 220 70% 50%;
+ --chart-2: 160 60% 45%;
+ --chart-3: 30 80% 55%;
+ --chart-4: 280 65% 60%;
+ --chart-5: 340 75% 55%;
+ --sidebar-background: 240 5.9% 10%;
+ --sidebar-foreground: 240 4.8% 95.9%;
+ --sidebar-primary: 224.3 76.3% 48%;
+ --sidebar-primary-foreground: 0 0% 100%;
+ --sidebar-accent: 240 3.7% 15.9%;
+ --sidebar-accent-foreground: 240 4.8% 95.9%;
+ --sidebar-border: 240 3.7% 15.9%;
+ --sidebar-ring: 217.2 91.2% 59.8%;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
diff --git a/2025/tailwind.config.ts b/2025/tailwind.config.ts
new file mode 100644
index 00000000..deaa2777
--- /dev/null
+++ b/2025/tailwind.config.ts
@@ -0,0 +1,80 @@
+import type { Config } from 'tailwindcss';
+
+const config = {
+ darkMode: 'class',
+ content: [
+ './pages/**/*.{ts,tsx}',
+ './components/**/*.{ts,tsx}',
+ './app/**/*.{ts,tsx}',
+ './src/**/*.{ts,tsx}',
+ '*.{js,ts,jsx,tsx,mdx}',
+ ],
+ prefix: '',
+ theme: {
+ container: {
+ center: true,
+ padding: '2rem',
+ screens: {
+ '2xl': '1400px',
+ },
+ },
+ extend: {
+ colors: {
+ border: 'hsl(var(--border))',
+ input: 'hsl(var(--input))',
+ ring: 'hsl(var(--ring))',
+ background: 'hsl(var(--background))',
+ foreground: 'hsl(var(--foreground))',
+ primary: {
+ DEFAULT: 'hsl(var(--primary))',
+ foreground: 'hsl(var(--primary-foreground))',
+ },
+ secondary: {
+ DEFAULT: 'hsl(var(--secondary))',
+ foreground: 'hsl(var(--secondary-foreground))',
+ },
+ destructive: {
+ DEFAULT: 'hsl(var(--destructive))',
+ foreground: 'hsl(var(--destructive-foreground))',
+ },
+ muted: {
+ DEFAULT: 'hsl(var(--muted))',
+ foreground: 'hsl(var(--muted-foreground))',
+ },
+ accent: {
+ DEFAULT: 'hsl(var(--accent))',
+ foreground: 'hsl(var(--accent-foreground))',
+ },
+ popover: {
+ DEFAULT: 'hsl(var(--popover))',
+ foreground: 'hsl(var(--popover-foreground))',
+ },
+ card: {
+ DEFAULT: 'hsl(var(--card))',
+ foreground: 'hsl(var(--card-foreground))',
+ },
+ },
+ borderRadius: {
+ lg: 'var(--radius)',
+ md: 'calc(var(--radius) - 2px)',
+ sm: 'calc(var(--radius) - 4px)',
+ },
+ keyframes: {
+ 'accordion-down': {
+ from: { height: '0' },
+ to: { height: 'var(--radix-accordion-content-height)' },
+ },
+ 'accordion-up': {
+ from: { height: 'var(--radix-accordion-content-height)' },
+ to: { height: '0' },
+ },
+ },
+ animation: {
+ 'accordion-down': 'accordion-down 0.2s ease-out',
+ 'accordion-up': 'accordion-up 0.2s ease-out',
+ },
+ },
+ },
+} satisfies Config;
+
+export default config;