posimai-root/docs/posimai-bg.md

7.7 KiB
Raw Blame History

Posimai 背景スタイル — コピペ用リファレンス

新規アプリで Station/Guard と同じ背景を使いたい時はここからコピーする。 AI への指示は「docs/posimai-bg.md の背景スタイルを適用して」だけで OK。


何が入っているか

要素 内容
背景色 #0C1221Termius 系ダークブルー)
グリッド線 48px 格子、極薄白
上部グロー シアン+アクセントカラーのグラデーション
バイナリオーロラ 0/1 が降るキャンバスアニメーションStation と同仕様)
フォント InterUI + JetBrains Monoコード・キャンバス

アクセントカラーだけ各アプリで差し替えるGuard は #F97316、Station は #22D3EE など)。


1. layout.tsx — フォント読み込み

import { Inter, JetBrains_Mono } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  weight: ['300', '400', '500', '600'],
  display: 'swap',
  variable: '--font-sans',
});

const jetbrainsMono = JetBrains_Mono({
  subsets: ['latin'],
  weight: ['400', '500'],
  display: 'swap',
  variable: '--font-mono',
});

// body の className に両方渡す
<body className={`${inter.variable} ${jetbrainsMono.variable}`}>

2. globals.css — 背景スタイル一式

@theme inline {
  --font-sans: 'Inter', system-ui, -apple-system, sans-serif;
  --font-mono: 'JetBrains Mono', 'Fira Code', monospace;
}

:root {
  --bg:       #0C1221;
  --surface:  #111827;
  --surface2: #1A2332;
  --border:   #1F2D40;
  /* アクセントカラーはアプリごとに変える */
  --accent:   #22D3EE;   /* 例: Station はシアン */
  --grid-line: rgba(255, 255, 255, 0.028);
}

body {
  background: var(--bg);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
}

/* グリッド線 */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: 48px 48px;
  pointer-events: none;
  z-index: 0;
}

/* 上部グロー(アクセントカラーを参照) */
body::after {
  content: '';
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 480px;
  background: radial-gradient(
    ellipse 70% 45% at 50% -5%,
    color-mix(in srgb, var(--accent) 8%, transparent) 0%,
    transparent 70%
  );
  pointer-events: none;
  z-index: 0;
}

/* コンテンツをオーロラの上に */
body > * {
  position: relative;
  z-index: 1;
}

3. BinaryAurora.tsx — コピペ用完全版

色帯BANDSの hue を変えるとアプリのカラーに合わせられる。 現在値は Station と同じシアン(185) / パープル(265) / グリーン(150)。

'use client';

import { useEffect, useRef } from 'react';

export function BinaryAurora() {
  const canvasRef = useRef<HTMLCanvasElement>(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    const FONT_SIZE = 14;

    // 色帯: hue を変えてアプリのカラーに合わせる
    // シアン/パープル/グリーンStation・Guard 共通)
    const BANDS = [
      { hue: 185, sat: 90, x: 0.15, speed: 0.00018, phase: 0   },
      { hue: 265, sat: 80, x: 0.38, speed: 0.00013, phase: 1.5 },
      { hue: 185, sat: 85, x: 0.62, speed: 0.00020, phase: 3.0 },
      { hue: 150, sat: 70, x: 0.80, speed: 0.00015, phase: 4.2 },
    ];

    type Col = { y: number; speed: number; len: number; chars: string[]; opacity: number };
    let cols: Col[] = [];
    let t = 0;
    let raf: number;

    const resize = () => {
      canvas.width  = window.innerWidth;
      canvas.height = window.innerHeight;
      const numCols = Math.ceil(canvas.width / FONT_SIZE);
      while (cols.length < numCols) {
        cols.push({
          y:       Math.random() * canvas.height,
          speed:   1.0 + Math.random() * 3.2,
          len:     8   + Math.floor(Math.random() * 22),
          chars:   [],
          opacity: 0.28 + Math.random() * 0.50,
        });
      }
      if (cols.length > numCols) cols.length = numCols;
    };

    resize();
    window.addEventListener('resize', resize);

    function getBandColor(x: number, now: number): { hue: number; sat: number; alpha: number } {
      const xf = x / (canvas?.width ?? 1);
      let best = BANDS[0];
      let bestDist = Infinity;
      for (const b of BANDS) {
        const bx   = b.x + Math.sin(now * b.speed + b.phase) * 0.12;
        const dist = Math.abs(xf - bx);
        if (dist < bestDist) { bestDist = dist; best = b; }
      }
      const bx    = best.x + Math.sin(now * best.speed + best.phase) * 0.12;
      const alpha = Math.max(0, 1 - Math.abs(xf - bx) / 0.22);
      return { hue: best.hue, sat: best.sat, alpha };
    }

    const draw = () => {
      t++;
      raf = requestAnimationFrame(draw);
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.font = `${FONT_SIZE}px 'JetBrains Mono', monospace`;

      cols.forEach((col, i) => {
        const x    = i * FONT_SIZE;
        const band = getBandColor(x, t);

        for (let j = 0; j < col.len; j++) {
          const cy = col.y - j * FONT_SIZE;
          if (cy < -FONT_SIZE || cy > canvas.height + FONT_SIZE) continue;

          if (!col.chars[j] || (t % 8 === 0 && Math.random() < 0.05)) {
            col.chars[j] = Math.random() < 0.5 ? '1' : '0';
          }
          const ch = col.chars[j];
          const trailAlpha = (1 - j / col.len) * col.opacity;
          const finalAlpha = trailAlpha * (band.alpha * 0.7 + 0.15);

          if (j === 0) {
            ctx.fillStyle = `hsla(${band.hue},${band.sat}%,94%,${Math.min(1, finalAlpha * 2.2)})`;
          } else if (ch === '1') {
            ctx.fillStyle = `hsla(${band.hue},${band.sat}%,65%,${finalAlpha})`;
          } else {
            ctx.fillStyle = `hsla(${(band.hue + 30) % 360},${Math.round(band.sat * 0.6)}%,45%,${finalAlpha * 0.55})`;
          }
          ctx.fillText(ch, x, cy);
        }

        col.y += col.speed;
        if (col.y - col.len * FONT_SIZE > canvas.height) {
          col.y      = -FONT_SIZE * 2;
          col.speed  = 1.0 + Math.random() * 3.2;
          col.len    = 8   + Math.floor(Math.random() * 22);
          col.chars  = [];
          col.opacity = 0.28 + Math.random() * 0.50;
        }
      });
    };

    raf = requestAnimationFrame(draw);
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener('resize', resize);
    };
  }, []);

  return (
    <canvas
      ref={canvasRef}
      aria-hidden="true"
      style={{
        position: 'fixed',
        inset: 0,
        opacity: 1,
        pointerEvents: 'none',
        zIndex: 0,
      }}
    />
  );
}

使い方

// layout.tsx または page.tsx に追加するだけ
import { BinaryAurora } from '@/components/BinaryAurora';

export default function Layout({ children }) {
  return (
    <>
      <BinaryAurora />
      {children}
    </>
  );
}

4. カスタマイズチートシート

変えたいもの 場所 変更内容
雨の色 BANDS[*].hue オレンジ系は 18〜42、シアンは 185、パープルは 265
雨の速さ speed: 1.0 + Math.random() * 3.2 数値を大きくすると速い
雨の長さ len: 8 + Math.floor(Math.random() * 22) 数値を大きくすると長いトレイル
背景色 --bg CSS 変数 #0C1221 標準。より暗くしたければ #080E1A など
上部グロー body::after--accent アクセントカラーに追従する
キャンバス全体の明るさ canvas opacity 0.5〜1.0 の範囲で調整