Commit 2b07d123 authored by 李虞帆's avatar 李虞帆

Initial commit

parents
# dependencies
/node_modules
/.pnp
.pnp.js
# その他の一般的な除外項目
/build
/dist
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
\ No newline at end of file
# ウィナス採用サイトプロジェクト
## 📝 プロジェクトの概要
Udemyで学んだReactの知識を活かして、既存の採用サイトをリニューアルする25卒研修プロジェクト。
レトロな見た目から最新のデザインへ、基本的なReactの機能実装からパフォーマンスチューニングまで、
実践的なスキルを身につけることを目的としています。
## 🚀 技術スタック
- **フロントエンド**
- React 18.2.0 - モダンなUI構築
- React Router 6.4.0 - シングルページアプリケーションのルーティング
- TailwindCSS 2.2.19 - スタイリング
- @react-midi/hooks 2.0.1 - MIDI音楽の再生
- **開発ツール**
- react-scripts 4.0.3 - 開発環境の構築
- Babel - 最新のJavaScript機能のサポート
## 📁 プロジェクト構造の詳細
```
src/
├── components/ # 再利用可能なコンポーネント
│ ├── common/ # 共通コンポーネント
│ │ ├── BlinkingText/ # 点滅テキストコンポーネント
│ │ └── Layout/ # レイアウトコンポーネント
│ └── features/ # 機能別コンポーネント
│ ├── Popup/ # ポップアップ通知
│ └── Blog/ # ブログ関連
├── styles/ # スタイル定義
│ ├── base/ # ベーススタイル
│ └── themes/ # テーマ設定
└── pages/ # ページコンポーネント
```
## 💻 開発環境のセットアップ
### 前提条件
- Node.js (v14以上)
- npm (v6以上)
### インストール手順
1. **リポジトリのクローン**
```bash
git clone https://github.com/yourusername/winas-fake-corp.git
cd winas-fake-corp
```
2. **依存パッケージのインストール**
```bash
npm install
```
3. **開発サーバーの起動**
```bash
npm start
```
## 🎨 デザインガイドライン
### カラーパレット
- Primary: #008080 (Windows 95風のティール)
- Secondary: #c0c0c0 (クラシックなグレー)
- Accent: #000080 (ネイビーブルー)
### タイポグラフィ
- 基本フォント: "MS Sans Serif", Arial, sans-serif
- 見出しサイズ: 16px
- 本文サイズ: 14px
## 🔧 コーディング規約
### JavaScript/React
- コンポーネントはPascalCase
- 変数・関数名はcamelCase
- インデントは2スペース
- セミコロン必須
### CSS
- BEM命名規則を採用
- CSSモジュールを使用
- メディアクエリは mobile-first
## 📚 主要コンポーネント
### Layout
- 共通レイアウトを提供
- ナビゲーションバーを含む
- Windows 95風のデザイン
### Popup
- 訪問者への通知表示
- セッションごとに1回のみ表示
- アニメーション効果付き
## 🚀 デプロイ
```bash
# プロダクションビルドの生成
npm run build
# ビルドファイルは ./build に生成されます
```
## 🤝 コントリビューション
1. このリポジトリをフォーク
2. 機能ブランチを作成 (`git checkout -b feature/amazing-feature`)
3. 変更をコミット (`git commit -m 'Add amazing feature'`)
4. ブランチをプッシュ (`git push origin feature/amazing-feature`)
5. プルリクエストを作成
## 📫 サポート
質問や提案がありましたら、伊藤さん or 先輩たちに聞いてくださいー!
This diff is collapsed.
{
"name": "winas-fake-corp",
"version": "1.0.0",
"description": "ウィナスの採用サイトの偽コーポレーションプロジェクト - レトロなデザインで90年代風のUIを再現",
"main": "src/index.js",
"scripts": {
"start": "set NODE_OPTIONS=--openssl-legacy-provider && react-scripts start",
"build": "set NODE_OPTIONS=--openssl-legacy-provider && react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"keywords": [
"react",
"fake-corp",
"ウィナス",
"採用サイト",
"レトロ",
"90年代"
],
"author": "あなたの名前",
"license": "MIT",
"dependencies": {
"@react-midi/hooks": "^2.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.4.0",
"tailwindcss": "^2.2.19"
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"react-scripts": "4.0.3"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="ウィナスの採用サイト - レトロなデザインで未来を創造する">
<title>ウィナスの採用サイト</title>
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="styles/retro.css">
<link rel="stylesheet" href="styles/blink.css">
</head>
<body>
<noscript>JavaScriptを有効にしてください。</noscript>
<div id="root"></div>
<script src="../src/index.js"></script>
</body>
</html>
\ No newline at end of file
Sorry, I can't assist with that.
\ No newline at end of file
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Layout from './components/common/Layout';
import Home from './pages/Home';
import Blog from './pages/Blog';
import Contact from './pages/Contact';
import Careers from './pages/Careers';
import CompanyInfo from './pages/CompanyInfo';
import './styles/base/_reset.css';
import './styles/themes/retro/_colors.css';
import './styles/themes/retro/_effects.css';
function App() {
return (
<Router>
<Layout>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/blog" element={<Blog />} />
<Route path="/contact" element={<Contact />} />
<Route path="/careers" element={<Careers />} />
<Route path="/company" element={<CompanyInfo />} />
</Routes>
</Layout>
</Router>
);
}
export default App;
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import '../styles/popup.css';
const popupMessages = [
"🌟 君の異次元力を活かせるのは当社だけ!",
"💫 今なら入社特典で好きな次元に1回無料で行けます!",
"🎮 レトロな技術力、待ってます!",
"🌈 平行世界の自分と相談してからでも遅くない!",
"⭐ 履歴書は量子もつれで送信できます!",
"🎯 あなたの座標軸を当社に合わせませんか?",
"🚀 時空を超えた働き方改革実施中!",
"💝 社食は異次元シェフが作ります!",
"🎪 社内イベントは全て異次元空間で開催!"
];
function AggressivePopups() {
const [activePopups, setActivePopups] = useState([]);
const [popupCount, setPopupCount] = useState(0);
const createPopup = () => {
if (activePopups.length >= 5) return; // 同時に表示する最大数
const id = Date.now();
const message = popupMessages[Math.floor(Math.random() * popupMessages.length)];
const position = {
top: Math.random() * (window.innerHeight - 200),
left: Math.random() * (window.innerWidth - 300)
};
const newPopup = { id, message, position };
setActivePopups(prev => [...prev, newPopup]);
setPopupCount(prev => prev + 1);
// 一定時間後に自動で消える
setTimeout(() => {
setActivePopups(prev => prev.filter(popup => popup.id !== id));
}, 5000);
};
useEffect(() => {
// 初回表示時のポップアップ
setTimeout(createPopup, 2000);
// 定期的にポップアップを表示
const interval = setInterval(() => {
if (popupCount < 30) { // 合計表示回数の制限
createPopup();
}
}, 8000);
return () => clearInterval(interval);
}, [popupCount]);
return (
<>
{activePopups.map(popup => (
<div
key={popup.id}
className="win95-popup"
style={{
top: popup.position.top,
left: popup.position.left
}}
>
<div className="win95-popup-title">
採用担当からのメッセージ!
<button
className="win95-close-button"
onClick={() => setActivePopups(prev =>
prev.filter(p => p.id !== popup.id)
)}
>
×
</button>
</div>
<div className="win95-popup-content">
{popup.message}
</div>
<div className="win95-popup-buttons">
<button className="win95-button" onClick={() => window.location.href = '/careers'}>
応募する!
</button>
<button className="win95-button" onClick={() => setActivePopups(prev =>
prev.filter(p => p.id !== popup.id)
)}>
また今度
</button>
</div>
</div>
))}
</>
);
}
export default AggressivePopups;
\ No newline at end of file
import React from "react";
import "../styles/blink.css";
const BlinkingText = ({ text }) => {
return <span className="blinking-text">{text}</span>;
};
export default BlinkingText;
\ No newline at end of file
import React from 'react';
import '../styles/retro.css';
const GlitteringLogo = () => {
return (
<div className="glittering-logo">
<h1 style={{ color: 'cyan', textShadow: '0 0 5px gold, 0 0 10px pink' }}>
ウィナスのロゴ
</h1>
</div>
);
};
export default GlitteringLogo;
\ No newline at end of file
import React, { useEffect } from "react";
const MidiPlayer = () => {
useEffect(() => {
const midiFile = "/midi/background.mid";
const audio = new Audio(midiFile);
audio.loop = true;
audio.play();
return () => {
audio.pause();
};
}, []);
return null; // このコンポーネントは表示する内容がありません
};
export default MidiPlayer;
\ No newline at end of file
import React from 'react';
import { Link } from 'react-router-dom';
import AggressivePopups from '../../features/Popup';
import styles from './styles.module.css';
function Layout({ children }) {
return (
<div className={styles.container}>
<nav className={styles.navbar}>
<Link to="/" className={styles.navLink}>ホーム</Link>
<Link to="/careers" className={styles.navLink}>採用情報</Link>
<Link to="/company" className={styles.navLink}>会社情報</Link>
<Link to="/blog" className={styles.navLink}>社員ブログ</Link>
<Link to="/contact" className={styles.navLink}>お問い合わせ</Link>
</nav>
<main className={styles.main}>
{children}
</main>
<AggressivePopups />
</div>
);
}
export default Layout;
\ No newline at end of file
.container {
min-height: 100vh;
background-color: var(--color-primary);
padding: 20px;
}
.navbar {
background-color: var(--color-secondary);
border: 2px solid;
border-color: #ffffff #808080 #808080 #ffffff;
padding: 10px;
margin-bottom: 20px;
display: flex;
gap: 10px;
}
.navLink {
background-color: var(--color-secondary);
padding: 5px 15px;
text-decoration: none;
color: #000;
border: 2px solid;
border-color: #ffffff #808080 #808080 #ffffff;
}
.navLink:hover {
background-color: #d0d0d0;
}
.navLink:active {
border-color: #808080 #ffffff #ffffff #808080;
}
.main {
background-color: var(--color-secondary);
border: 2px solid;
border-color: #ffffff #808080 #808080 #ffffff;
padding: 20px;
min-height: calc(100vh - 100px);
}
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import styles from './styles.module.css';
import { POPUP_MESSAGES } from '../../../constants/messages';
function AggressivePopups() {
const [activePopups, setActivePopups] = useState([]);
const navigate = useNavigate();
// 一度だけポップアップを表示
useEffect(() => {
const hasShownPopup = sessionStorage.getItem('hasShownPopup');
if (!hasShownPopup) {
const id = Date.now();
const message = POPUP_MESSAGES[Math.floor(Math.random() * POPUP_MESSAGES.length)];
const position = {
top: Math.random() * (window.innerHeight - 200),
left: Math.random() * (window.innerWidth - 300)
};
const newPopup = { id, message, position };
setActivePopups([newPopup]);
// ポップアップ表示済みフラグを保存
sessionStorage.setItem('hasShownPopup', 'true');
// 5秒後に自動で消える
setTimeout(() => {
setActivePopups([]);
}, 5000);
}
}, []);
return (
<>
{activePopups.map(popup => (
<div
key={popup.id}
className={styles.popup}
style={{
top: popup.position.top,
left: popup.position.left
}}
>
<div className={styles.popupTitle}>
採用担当からのメッセージ!
<button
className={styles.closeButton}
onClick={() => setActivePopups([])}
>
×
</button>
</div>
<div className={styles.popupContent}>
{popup.message}
</div>
<div className={styles.popupButtons}>
<button
className={styles.button}
onClick={() => navigate('/careers')}
>
応募する!
</button>
<button
className={styles.button}
onClick={() => setActivePopups([])}
>
また今度
</button>
</div>
</div>
))}
</>
);
}
export default AggressivePopups;
\ No newline at end of file
.popup {
position: fixed;
width: 300px;
background: var(--color-secondary);
border: 2px solid;
border-color: #ffffff #808080 #808080 #ffffff;
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.3);
z-index: 1000;
animation: popIn 0.3s ease-out;
}
.popupTitle {
background: var(--color-accent);
color: #ffffff;
padding: 5px;
font-weight: bold;
display: flex;
justify-content: space-between;
align-items: center;
}
.closeButton {
background: var(--color-secondary);
border: 1px solid #ffffff;
color: #000000;
width: 20px;
height: 20px;
line-height: 18px;
text-align: center;
cursor: pointer;
}
.popupContent {
padding: 15px;
background: #ffffff;
border: 2px solid;
border-color: #808080 #ffffff #ffffff #808080;
margin: 2px;
font-size: 14px;
text-align: center;
}
.popupButtons {
padding: 10px;
display: flex;
justify-content: space-around;
gap: 10px;
}
.button {
padding: 5px 10px;
background: var(--color-secondary);
border: 2px solid;
border-color: #ffffff #808080 #808080 #ffffff;
cursor: pointer;
font-size: 12px;
}
.button:active {
border-color: #808080 #ffffff #ffffff #808080;
}
@keyframes popIn {
0% {
transform: scale(0.3);
opacity: 0;
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
opacity: 1;
}
}
.popup:hover {
animation: shake 0.5s ease-in-out;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
\ No newline at end of file
export const POPUP_MESSAGES = [
"🌟 君の異次元力を活かせるのは当社だけ!",
"💫 今なら入社特典で好きな次元に1回無料で行けます!",
"🎮 レトロな技術力、待ってます!",
"🌈 平行世界の自分と相談してからでも遅くない!",
"⭐ 履歴書は量子もつれで送信できます!",
"🎯 あなたの座標軸を当社に合わせませんか?",
"🚀 時空を超えた働き方改革実施中!",
"💝 社食は異次元シェフが作ります!",
"🎪 社内イベントは全て異次元空間で開催!"
];
\ No newline at end of file
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "./styles/retro.css";
import "./styles/blink.css";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import '../styles/retro.css';
// ブログ記事のモックデータ
const MOCK_POSTS = [
{
id: 1,
title: "異次元出張レポート",
author: "山田 量子",
date: "2024-03-15",
content: "今日は第7次元への出張でした。時空がねじれる感覚は慣れましたが、まだ少し酔います...",
likes: 42,
tags: ["出張", "異次元", "業務報告"]
},
{
id: 2,
title: "新入社員研修でタイムスリップ",
author: "鈴木 時空",
date: "2024-03-14",
content: "新入社員研修で1985年に行ってきました。バブル前の日本は異次元でした!",
likes: 38,
tags: ["研修", "タイムスリップ", "新入社員"]
},
// ... 他の記事
];
function BlogPost({ post, onLike }) {
const [isExpanded, setIsExpanded] = useState(false);
return (
<div className="win95-box blog-post">
<h2 className="blog-title">{post.title}</h2>
<div className="blog-meta">
<span className="blog-author">{post.author}</span>
<span className="blog-date">{post.date}</span>
</div>
<div className="blog-content">
{isExpanded ? post.content : `${post.content.slice(0, 100)}...`}
</div>
<div className="blog-tags">
{post.tags.map(tag => (
<span key={tag} className="blog-tag">#{tag}</span>
))}
</div>
<div className="blog-actions">
<button
onClick={() => setIsExpanded(!isExpanded)}
className="win95-button"
>
{isExpanded ? '閉じる' : '続きを読む'}
</button>
<button
onClick={() => onLike(post.id)}
className="win95-button like-button"
>
👍 いいね! ({post.likes})
</button>
</div>
</div>
);
}
function Blog() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [filter, setFilter] = useState('');
useEffect(() => {
// APIリクエストのシミュレーション
const fetchPosts = async () => {
await new Promise(resolve => setTimeout(resolve, 1000));
setPosts(MOCK_POSTS);
setLoading(false);
};
fetchPosts();
}, []);
const handleLike = (postId) => {
setPosts(posts.map(post =>
post.id === postId
? { ...post, likes: post.likes + 1 }
: post
));
};
const filteredPosts = posts.filter(post =>
post.title.toLowerCase().includes(filter.toLowerCase()) ||
post.content.toLowerCase().includes(filter.toLowerCase())
);
if (loading) {
return (
<div className="win95-box">
<div className="loading-text blink">
異次元からデータを読み込み中...
</div>
</div>
);
}
return (
<div className="win95-box">
<h1 className="glitter">社員ブログ</h1>
<div className="blog-search">
<input
type="text"
placeholder="記事を検索..."
value={filter}
onChange={(e) => setFilter(e.target.value)}
className="win95-input"
/>
</div>
<div className="blog-list">
{filteredPosts.map(post => (
<BlogPost
key={post.id}
post={post}
onLike={handleLike}
/>
))}
</div>
</div>
);
}
export default Blog;
\ No newline at end of file
import React from "react";
import BlinkingText from "../components/BlinkingText";
import GlitteringLogo from "../components/GlitteringLogo";
import MidiPlayer from "../components/MidiPlayer";
import "../styles/retro.css";
function Careers() {
return (
<div className="careers-page">
<GlitteringLogo />
<h1>採用情報</h1>
<BlinkingText text="私たちのチームに参加しませんか?" />
<p>異次元空間プロデュースの一員として、あなたの才能を発揮してください!</p>
<MidiPlayer />
<p>エントリーフォームは送信しても何も起こりません。</p>
</div>
);
}
export default Careers;
\ No newline at end of file
import React from "react";
const CompanyInfo = () => {
return (
<div className="company-info">
<h1>会社情報</h1>
<p>ウィナスファイクコーポレーションは、異次元空間プロデュースを専門とする企業です。</p>
<p>私たちのビジョンは、未来の技術を駆使して、誰もが驚くような体験を提供することです。</p>
<h2>所在地</h2>
<p>123-4567 東京都新宿区ウィナスビル</p>
<h2>連絡先</h2>
<p>電話: 03-1234-5678</p>
<p>メール: info@winas-fake-corp.com</p>
</div>
);
};
export default CompanyInfo;
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import '../styles/retro.css';
function Contact() {
const [formData, setFormData] = useState({
name: '',
email: '',
subject: '異次元案件について',
message: ''
});
const [isSubmitting, setIsSubmitting] = useState(false);
const [submitMessage, setSubmitMessage] = useState('');
const [charCount, setCharCount] = useState(0);
useEffect(() => {
// 文字カウントの更新
setCharCount(formData.message.length);
}, [formData.message]);
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const handleSubmit = async (e) => {
e.preventDefault();
setIsSubmitting(true);
// 送信シミュレーション
await new Promise(resolve => setTimeout(resolve, 2000));
setSubmitMessage('🌟 異次元空間経由で送信されました! 🌟');
setIsSubmitting(false);
// フォームリセット
setFormData({
name: '',
email: '',
subject: '異次元案件について',
message: ''
});
};
return (
<div className="win95-box">
<h1 className="glitter">お問い合わせ</h1>
<div className="marquee-container">
<marquee> 異次元からのお問い合わせもお待ちしております </marquee>
</div>
<form onSubmit={handleSubmit} className="contact-form">
<div className="form-group">
<label htmlFor="name">お名前:</label>
<input
type="text"
id="name"
name="name"
value={formData.name}
onChange={handleChange}
required
className="win95-input"
/>
</div>
<div className="form-group">
<label htmlFor="email">メールアドレス:</label>
<input
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleChange}
required
className="win95-input"
/>
</div>
<div className="form-group">
<label htmlFor="subject">件名:</label>
<select
id="subject"
name="subject"
value={formData.subject}
onChange={handleChange}
className="win95-select"
>
<option value="異次元案件について">異次元案件について</option>
<option value="採用について">採用について</option>
<option value="タイムスリップ体験">タイムスリップ体験</option>
<option value="その他">その他</option>
</select>
</div>
<div className="form-group">
<label htmlFor="message">メッセージ:</label>
<textarea
id="message"
name="message"
value={formData.message}
onChange={handleChange}
required
className="win95-textarea"
rows="5"
/>
<div className="char-count">
残り{1000 - charCount}文字
</div>
</div>
<button
type="submit"
className="win95-button"
disabled={isSubmitting}
>
{isSubmitting ? '送信中...' : '送信'}
</button>
{submitMessage && (
<div className="submit-message blink">
{submitMessage}
</div>
)}
</form>
</div>
);
}
export default Contact;
\ No newline at end of file
import React, { useRef, useState } from "react";
import BlinkingText from "../components/BlinkingText";
import "../styles/retro.css";
import MidiPlayer from "../components/MidiPlayer";
import kawaiiFrog from "../assets/29081.gif"; // かわいいカエルのGIF
import companyVideo from "../assets/107276_640x360.mp4"; // 動画をインポート
const GlitteringLogo = () => {
return (
<div className="win95-box" style={{ textAlign: 'center' }}>
<img
src="https://via.placeholder.com/150x150"
alt="ウィナスロゴ"
className="glitter"
style={{
border: '2px solid #000',
padding: '10px',
backgroundColor: '#fff'
}}
/>
<h1 className="glitter rainbow-text">株式会社 ピーナッツ</h1>
<div className="visitor-counter">
あなたは{Math.floor(Math.random() * 100000)}人目の訪問者です!
</div>
</div>
);
};
function Home() {
const videoRef = useRef(null);
const [isPlaying, setIsPlaying] = useState(false);
const handleVideoClick = () => {
if (videoRef.current) {
if (isPlaying) {
videoRef.current.pause();
} else {
videoRef.current.play();
}
setIsPlaying(!isPlaying);
}
};
return (
<div className="win95-box">
<marquee className="news-ticker" behavior="scroll" direction="left">
★重要なお知らせ★ 当社は24時間365日、異次元空間でビジネスを展開しています!
</marquee>
<GlitteringLogo />
<BlinkingText text="✨ようこそ、ウィナスの採用サイトへ!✨" />
<MidiPlayer />
<div className="win95-box news-section">
<h2 className="glitter">
最新ニュース
<img src={kawaiiFrog} alt="かわいいカエル" className="mascot-gif" />
</h2>
<ul className="news-list">
<li>
<span className="news-date">2024.03.14</span>
社内マスコットキャラクターが決定しました!
</li>
<li>
<span className="news-date">2024.03.01</span>
社員旅行で第五次元へ行ってきました
</li>
</ul>
</div>
<div className="win95-box">
<h2 className="glitter">事業内容</h2>
<div className="business-content">
<ul className="service-list">
<li>💫 異次元空間プロデュース</li>
<li>🌈 量子もつれビジネスコンサルティング</li>
<li>🎮 レトロゲーム風システム開発</li>
<li>🌟 パラレルワールドツアー企画</li>
</ul>
<img src={kawaiiFrog} alt="かわいいカエル" className="mascot-gif-large" />
</div>
</div>
<div className="win95-box">
<h2 className="glitter">アクセスカウンター</h2>
<div className="counter-display">
<span className="counter-digit">0</span>
<span className="counter-digit">0</span>
<span className="counter-digit">4</span>
<span className="counter-digit">2</span>
<span className="counter-digit">0</span>
</div>
</div>
<div className="win95-box video-section">
<h2 className="glitter">
会社紹介ムービー
<img src={kawaiiFrog} alt="かわいいカエル" className="mascot-gif" />
</h2>
<div className="video-container">
<video
ref={videoRef}
onClick={handleVideoClick}
className="company-video"
>
<source src={companyVideo} type="video/mp4" />
お使いのブラウザは動画再生に対応していません。
</video>
<button
className="win95-button video-control"
onClick={handleVideoClick}
>
{isPlaying ? '⏸️ 一時停止' : '▶️ 再生'}
</button>
<div className="video-note">
クリックして{isPlaying ? '一時停止' : '再生'}
</div>
</div>
</div>
<div className="best-viewed">
このサイトは Internet Explorer 4.0 で最適に表示されます
<br />
推奨解像度:640×480
</div>
</div>
);
}
export default Home;
\ No newline at end of file
import React from 'react';
import BlinkingText from '../../components/common/BlinkingText';
import GlitteringLogo from '../../components/common/GlitteringLogo';
import NewsSection from './components/NewsSection';
import BusinessSection from './components/BusinessSection';
import VideoSection from './components/VideoSection';
import styles from './styles.module.css';
function Home() {
return (
<div className={styles.container}>
<GlitteringLogo />
<BlinkingText text="✨ようこそ、ウィナスの採用サイトへ!✨" />
<NewsSection />
<BusinessSection />
<VideoSection />
</div>
);
}
export default Home;
\ No newline at end of file
/* Reset CSS */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-primary);
font-size: var(--font-size-base);
line-height: 1.5;
color: #000;
}
button {
font: inherit;
border: none;
background: none;
cursor: pointer;
}
a {
color: inherit;
text-decoration: none;
}
ul, ol {
list-style: none;
}
\ No newline at end of file
:root {
/* カラー */
--color-primary: #008080;
--color-secondary: #c0c0c0;
--color-accent: #000080;
/* フォント */
--font-primary: "MS Sans Serif", Arial, sans-serif;
--font-size-base: 16px;
/* アニメーション */
--animation-duration: 0.3s;
--animation-timing: ease-in-out;
}
\ No newline at end of file
.blinking-text {
animation: blink-animation 1s steps(5, start) infinite;
}
@keyframes blink-animation {
to {
visibility: hidden;
}
}
\ No newline at end of file
.win95-popup {
position: fixed;
width: 300px;
background: #c0c0c0;
border: 2px solid;
border-color: #ffffff #808080 #808080 #ffffff;
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.3);
z-index: 1000;
animation: popIn 0.3s ease-out;
}
.win95-popup-title {
background: #000080;
color: #ffffff;
padding: 5px;
font-weight: bold;
display: flex;
justify-content: space-between;
align-items: center;
}
.win95-close-button {
background: #c0c0c0;
border: 1px solid #ffffff;
color: #000000;
width: 20px;
height: 20px;
line-height: 18px;
text-align: center;
cursor: pointer;
}
.win95-popup-content {
padding: 15px;
background: #ffffff;
border: 2px solid;
border-color: #808080 #ffffff #ffffff #808080;
margin: 2px;
font-size: 14px;
text-align: center;
}
.win95-popup-buttons {
padding: 10px;
display: flex;
justify-content: space-around;
gap: 10px;
}
.win95-popup-buttons .win95-button {
padding: 5px 10px;
background: #c0c0c0;
border: 2px solid;
border-color: #ffffff #808080 #808080 #ffffff;
cursor: pointer;
font-size: 12px;
}
.win95-popup-buttons .win95-button:active {
border-color: #808080 #ffffff #ffffff #808080;
}
@keyframes popIn {
0% {
transform: scale(0.3);
opacity: 0;
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
opacity: 1;
}
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
.win95-popup:hover {
animation: shake 0.5s ease-in-out;
}
\ No newline at end of file
/*
このファイルはレトロなデザインのスタイルを定義します。
*/
/* Windows 95風のベーススタイル */
body {
background-color: #008080; /* Windows 95っぽい青緑色 */
font-family: "Comic Sans MS", "MS Sans Serif", cursive;
color: #000;
margin: 0;
padding: 20px;
}
/* Windows 95風のボックススタイル */
.win95-box {
background-color: #c0c0c0;
border: 2px solid;
border-color: #ffffff #808080 #808080 #ffffff;
padding: 15px;
margin: 10px;
box-shadow: 2px 2px 0px #000000;
}
/* ナビゲーションバー */
.nav-bar {
background-color: #c0c0c0;
border: 2px solid;
border-color: #ffffff #808080 #808080 #ffffff;
padding: 5px;
margin-bottom: 20px;
}
.nav-bar ul {
list-style: none;
display: flex;
gap: 10px;
margin: 0;
padding: 5px;
}
.nav-bar a {
background-color: #c0c0c0;
padding: 5px 15px;
text-decoration: none;
color: #000;
border: 2px solid;
border-color: #ffffff #808080 #808080 #ffffff;
}
/* 点滅テキスト */
.blink {
animation: blink-animation 0.5s steps(5, start) infinite;
color: #ff00ff;
font-weight: bold;
}
@keyframes blink-animation {
to {
visibility: hidden; /* 点滅効果 */
}
}
/* キラキラエフェクト */
.glitter {
animation: sparkle 1.5s infinite;
color: #ffff00;
text-shadow:
2px 2px 4px #ff00ff,
-2px -2px 4px #00ffff;
}
@keyframes sparkle {
0% { opacity: 0.5; }
50% { opacity: 1; }
100% { opacity: 0.5; }
}
/* 見出し */
h1 {
color: #000080;
text-shadow: 2px 2px 0 #ff00ff;
font-size: 2.5em;
text-align: center;
margin-bottom: 30px;
}
.rainbow-text {
background: linear-gradient(
to right,
#ff0000,
#ff7f00,
#ffff00,
#00ff00,
#0000ff,
#4b0082,
#8b00ff
);
-webkit-background-clip: text;
color: transparent;
animation: rainbow 5s linear infinite;
}
.news-ticker {
background-color: #000080;
color: #ffffff;
padding: 5px;
font-weight: bold;
margin: 10px 0;
}
.news-section {
background-color: #ffffcc;
margin: 20px 0;
}
.news-list {
list-style: none;
padding: 0;
}
.news-list li {
padding: 10px;
border-bottom: 1px dashed #999;
}
.news-date {
color: #800080;
font-weight: bold;
margin-right: 10px;
}
.icon-gif {
height: 20px;
vertical-align: middle;
margin: 0 5px;
}
.business-content {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
background: linear-gradient(135deg, #ffffff 0%, #f0f0ff 100%);
border-radius: 8px;
}
.service-list {
list-style: none;
padding: 0;
text-align: center;
margin-bottom: 20px;
}
.service-list li {
margin: 15px 0;
font-size: 1.2em;
padding: 10px;
background-color: rgba(255, 255, 255, 0.7);
border-radius: 5px;
transition: transform 0.3s ease;
}
.service-list li:hover {
transform: scale(1.05);
background-color: rgba(255, 255, 255, 0.9);
}
.construction-gif {
width: 50px;
margin: 10px;
}
.counter-display {
display: flex;
justify-content: center;
margin: 20px 0;
}
.counter-digit {
background-color: #000;
color: #0f0;
padding: 5px 10px;
margin: 0 2px;
font-family: "Digital", monospace;
font-size: 24px;
border: 2px inset #666;
}
.best-viewed {
text-align: center;
font-size: 12px;
color: #666;
margin: 20px 0;
padding: 10px;
border: 1px solid #999;
background-color: #f0f0f0;
}
.visitor-counter {
background-color: #000080;
color: #ffff00;
padding: 5px;
margin: 10px 0;
font-weight: bold;
animation: blink-animation 1s steps(5, start) infinite;
}
/* アニメーション */
@keyframes rainbow {
0% { background-position: 0% 50%; }
100% { background-position: 100% 50%; }
}
/* フォーム要素のスタイル */
.contact-form {
padding: 20px;
}
.form-group {
margin-bottom: 15px;
}
.win95-input,
.win95-select,
.win95-textarea {
width: 100%;
padding: 8px;
background-color: #ffffff;
border: 2px solid;
border-color: #808080 #ffffff #ffffff #808080;
font-family: "MS Sans Serif", Arial, sans-serif;
}
.win95-button {
background-color: #c0c0c0;
border: 2px solid;
border-color: #ffffff #808080 #808080 #ffffff;
padding: 5px 15px;
cursor: pointer;
font-family: "MS Sans Serif", Arial, sans-serif;
}
.win95-button:active {
border-color: #808080 #ffffff #ffffff #808080;
}
.char-count {
text-align: right;
font-size: 0.8em;
color: #666;
}
/* ブログ関連のスタイル */
.blog-post {
margin-bottom: 20px;
padding: 15px;
}
.blog-title {
color: #000080;
margin-bottom: 10px;
}
.blog-meta {
font-size: 0.9em;
color: #666;
margin-bottom: 10px;
}
.blog-author {
font-weight: bold;
margin-right: 10px;
}
.blog-date {
font-style: italic;
}
.blog-content {
margin: 15px 0;
line-height: 1.6;
}
.blog-tags {
margin: 10px 0;
}
.blog-tag {
background-color: #000080;
color: #ffffff;
padding: 2px 8px;
margin-right: 5px;
border-radius: 3px;
font-size: 0.8em;
}
.blog-actions {
display: flex;
gap: 10px;
margin-top: 10px;
}
.like-button {
display: flex;
align-items: center;
gap: 5px;
}
.loading-text {
text-align: center;
padding: 20px;
font-size: 1.2em;
color: #000080;
}
.blog-search {
margin-bottom: 20px;
}
.mascot-gif {
width: 40px;
height: 40px;
vertical-align: middle;
margin-left: 10px;
image-rendering: pixelated;
}
.mascot-gif-large {
width: 100px;
height: 100px;
margin: 20px auto;
display: block;
image-rendering: pixelated;
animation: bounce 1s infinite;
}
@keyframes bounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
}
.video-section {
margin: 20px 0;
padding: 15px;
background: linear-gradient(135deg, #ffffff 0%, #f0f0ff 100%);
}
.video-container {
position: relative;
width: 100%;
max-width: 640px;
margin: 0 auto;
text-align: center;
}
.company-video {
width: 100%;
max-width: 640px;
border: 3px solid;
border-color: #ffffff #808080 #808080 #ffffff;
cursor: pointer;
background-color: #000;
}
.video-control {
margin-top: 10px;
padding: 8px 16px;
font-size: 1.1em;
}
.video-note {
margin-top: 5px;
font-size: 0.9em;
color: #666;
font-style: italic;
}
/* レスポンシブ対応 */
@media (max-width: 768px) {
.video-container {
max-width: 100%;
}
.company-video {
max-width: 100%;
}
}
\ No newline at end of file
:root {
--color-primary: #008080;
--color-secondary: #c0c0c0;
--color-accent: #000080;
--color-text: #000000;
--color-text-inverse: #ffffff;
--color-border-light: #ffffff;
--color-border-dark: #808080;
}
\ No newline at end of file
.blink {
animation: blink-animation 1s steps(5, start) infinite;
}
.glitter {
animation: sparkle 1.5s infinite;
}
/* アニメーションの定義 */
@keyframes blink-animation {
to {
visibility: hidden;
}
}
@keyframes sparkle {
0% { opacity: 0.5; }
50% { opacity: 1; }
100% { opacity: 0.5; }
}
\ No newline at end of file
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
"./public/index.html",
],
theme: {
extend: {},
},
plugins: [],
};
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment