Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
68648d7
feat: add CongYou ESG single-page website
claude Apr 18, 2026
0c12931
feat(esg-website): major upgrade with data dashboard, Lucide icons & …
claude Apr 18, 2026
63f977a
feat(esg-website): add zh/en language toggle, IndustrySolutions secti…
claude Apr 18, 2026
37e62aa
feat(esg-website): add ESG Vision, FAQ, scroll progress bar & back-to…
claude Apr 25, 2026
740c622
ci: add GitHub Actions workflow for ESG website deployment
claude Apr 25, 2026
bca2a63
style: Japanese Zen + green eco visual redesign
claude Apr 26, 2026
c005236
feat: expand products to 6, rename to match folder names
claude Apr 26, 2026
38e5f29
feat: connect ContactForm to Google Apps Script for order submissions
choujame Apr 26, 2026
04c2d05
fix: switch form submission to POST with text/plain for Apps Script c…
choujame Apr 26, 2026
e4ed065
feat: add real product photos from Google Drive to product tabs
choujame Apr 26, 2026
e78f051
feat: 樣品申請支援複數品項(動態新增/移除)
choujame Apr 27, 2026
219aa79
fix: send items as array to GAS instead of joined string
choujame Apr 27, 2026
c64cd24
fix: update product list to match current offerings
choujame Apr 27, 2026
f45eb62
feat: restructure FAQ with per-product categories and content
choujame Apr 27, 2026
6f59eab
feat: add per-product comparison tables with tab navigation
choujame Apr 27, 2026
d2b499b
feat: add 3 industry scenarios, update bio bag heat resistance and PG…
choujame Apr 27, 2026
bfc58fc
feat: migrate deployment to choujame.github.io
choujame Apr 30, 2026
f17bb3c
add GA4 tracking (G-WHKFDF3GFS) and form conversion event
choujame May 5, 2026
10f5752
fix GA4 Measurement ID to G-ZVJFVF3RDP
choujame May 5, 2026
31b9bbe
add favicon and verify GA4 deployment
choujame May 5, 2026
84ec6fb
fix GA4 ID to correct 琮祐ESG property (G-WHKFDF3GFS)
choujame May 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/deploy-esg.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: 🌿 Deploy CongYou ESG Website

on:
push:
branches:
- claude/build-esg-website-a30rS
paths:
- 'esg-website/**'
- '.github/workflows/deploy-esg.yml'
workflow_dispatch:

permissions:
contents: write

jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
working-directory: esg-website
run: npm install

- name: Build
working-directory: esg-website
run: npm run build

- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
personal_token: ${{ secrets.DEPLOY_TOKEN }}
external_repository: choujame/choujame.github.io
publish_branch: main
publish_dir: ./esg-website/dist
commit_message: 'deploy: CongYou ESG website'
4 changes: 4 additions & 0 deletions esg-website/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
dist/
.env
.DS_Store
27 changes: 27 additions & 0 deletions esg-website/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>琮祐企業 — CongYou ESG | 石頭紙系列</title>
<meta name="description" content="琮祐企業以天然礦石為原料,生產防水抗凍、低碳環保的石頭紙系列產品。無塑永續,由石開始。" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@200;300;400;500;600&family=Noto+Sans+TC:wght@200;300;400;500;600&display=swap"
rel="stylesheet"
/>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🌿</text></svg>" />
<script async src="https://www.googletagmanager.com/gtag/js?id=G-WHKFDF3GFS"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-WHKFDF3GFS');
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
23 changes: 23 additions & 0 deletions esg-website/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "congyou-esg",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"framer-motion": "^11.3.0",
"lucide-react": "^0.400.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.19",
"postcss": "^8.4.39",
"tailwindcss": "^3.4.4",
"vite": "^5.3.1"
}
}
6 changes: 6 additions & 0 deletions esg-website/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
Binary file added esg-website/public/images/products/bio-bag.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added esg-website/public/images/products/box.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added esg-website/public/images/products/buffer.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added esg-website/public/images/products/tableware.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added esg-website/public/images/products/vest-bag.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 40 additions & 0 deletions esg-website/src/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { LanguageProvider } from './context/LanguageContext'
import ScrollProgress from './components/ScrollProgress'
import BackToTop from './components/BackToTop'
import Navbar from './components/Navbar'
import Hero from './components/Hero'
import DataDashboard from './components/DataDashboard'
import IndustrySolutions from './components/IndustrySolutions'
import Features from './components/Features'
import ProductTabs from './components/ProductTabs'
import ComparisonTable from './components/ComparisonTable'
import ESGVision from './components/ESGVision'
import FAQ from './components/FAQ'
import Certification from './components/Certification'
import ContactForm from './components/ContactForm'
import Footer from './components/Footer'

export default function App() {
return (
<LanguageProvider>
<div className="min-h-screen bg-minimal-white font-sans antialiased">
<ScrollProgress />
<Navbar />
<main>
<Hero />
<DataDashboard />
<IndustrySolutions />
<Features />
<ProductTabs />
<ComparisonTable />
<ESGVision />
<FAQ />
<Certification />
<ContactForm />
</main>
<Footer />
<BackToTop />
</div>
</LanguageProvider>
)
}
33 changes: 33 additions & 0 deletions esg-website/src/components/BackToTop.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useState, useEffect } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { ArrowUp } from 'lucide-react'

export default function BackToTop() {
const [show, setShow] = useState(false)

useEffect(() => {
const fn = () => setShow(window.scrollY > 600)
window.addEventListener('scroll', fn, { passive: true })
return () => window.removeEventListener('scroll', fn)
}, [])

return (
<AnimatePresence>
{show && (
<motion.button
initial={{ opacity: 0, y: 12, scale: 0.85 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 12, scale: 0.85 }}
transition={{ duration: 0.25 }}
whileHover={{ scale: 1.08 }}
whileTap={{ scale: 0.95 }}
onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
className="fixed bottom-8 right-8 z-40 w-11 h-11 bg-forest text-white rounded-full flex items-center justify-center shadow-xl shadow-forest/25 hover:bg-forest-dark transition-colors duration-200"
aria-label="Back to top"
>
<ArrowUp size={16} strokeWidth={2} />
</motion.button>
)}
</AnimatePresence>
)
}
129 changes: 129 additions & 0 deletions esg-website/src/components/Certification.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { motion } from 'framer-motion'
import FadeIn from './FadeIn'
import { useLanguage } from '../context/LanguageContext'

const certs = [
{ abbr: 'SGS', name: 'SGS', desc: { zh: '國際品質驗證', en: 'International Quality' } },
{ abbr: 'FDA', name: 'FDA', desc: { zh: '美國食品安全', en: 'US Food Safety' } },
{ abbr: 'ISO\n14001', name: 'ISO 14001', desc: { zh: '環境管理系統', en: 'Environmental Mgmt' } },
{ abbr: 'RoHS', name: 'RoHS', desc: { zh: '歐盟有害物質限制', en: 'EU Hazardous Restriction' } },
{ abbr: 'CE', name: 'CE', desc: { zh: '歐盟合規標誌', en: 'EU Conformity Mark' } },
{ abbr: '環保\n標章', name: { zh: '台灣環保標章', en: 'Taiwan Eco Label' }, desc: { zh: '通過環保署認證', en: 'EPA Certified' } },
{ abbr: 'Carbon\nFP', name: { zh: '碳足跡標籤', en: 'Carbon Footprint' }, desc: { zh: 'Carbon Footprint Label', en: 'Carbon Footprint Label' } },
{ abbr: 'BSCI', name: 'BSCI', desc: { zh: '商業社會責任', en: 'Business Social Compliance' } },
]

const partners = [
{ name: '鴻海集團', en: 'Foxconn' },
{ name: '統一企業', en: 'Uni-President' },
{ name: '全聯福利中心', en: 'PX Mart' },
{ name: '家樂福', en: 'Carrefour' },
{ name: '順豐速運', en: 'SF Express' },
{ name: 'MUJI', en: '無印良品' },
{ name: '博客來', en: 'Books.com.tw' },
{ name: 'Shopee', en: '蝦皮購物' },
{ name: 'DHL', en: '敦豪快遞' },
{ name: 'IKEA', en: '宜家家居' },
]

const COPY = {
zh: {
eyebrow: 'Certifications',
title: '國際認證標章',
subtitle: '通過全球最嚴格第三方機構驗證,品質與環保雙重保障。',
partnersLabel: 'Trusted Partners',
partnersTitle: '全球品牌信賴夥伴',
},
en: {
eyebrow: 'Certifications',
title: 'Certifications',
subtitle: 'Verified by the world\'s most rigorous third-party institutions — quality and sustainability guaranteed.',
partnersLabel: 'Trusted Partners',
partnersTitle: 'Trusted by Global Brands',
},
}

const STATS = {
zh: [{ v: '8+', l: '國際認證' }, { v: '15+', l: '年產業經驗' }, { v: '30+', l: '服務國家' }, { v: '500+', l: '合作企業' }],
en: [{ v: '8+', l: 'Certifications' }, { v: '15+', l: 'Years Experience' }, { v: '30+', l: 'Countries Served' }, { v: '500+', l: 'Partners' }],
}

function CertCard({ cert, lang }) {
const name = typeof cert.name === 'object' ? cert.name[lang] : cert.name
const desc = cert.desc[lang]
return (
<div className="flex-shrink-0 mx-3 w-40 bg-white/[0.08] hover:bg-white/[0.14] border border-white/[0.12] hover:border-white/[0.22] rounded-2xl p-5 flex flex-col items-center gap-3 transition-all duration-300">
<div className="w-14 h-14 rounded-xl bg-white/[0.12] flex items-center justify-center">
<span className="text-white font-light text-[11px] whitespace-pre-line text-center leading-tight">{cert.abbr}</span>
</div>
<div className="text-center">
<p className="text-white text-xs font-light">{name}</p>
<p className="text-white/45 text-[10px] font-light mt-0.5">{desc}</p>
</div>
</div>
)
}

export default function Certification() {
const { lang } = useLanguage()
const c = COPY[lang]
const stats = STATS[lang]
const duplicated = [...certs, ...certs]

return (
<section id="certifications" className="py-28 bg-forest overflow-hidden">
<div className="max-w-6xl mx-auto px-6">
<FadeIn className="text-center mb-14">
<p className="text-white/40 text-[10px] tracking-[0.28em] uppercase mb-3 font-light">{c.eyebrow}</p>
<h2 className="text-3xl md:text-4xl font-light text-white mb-4">{c.title}</h2>
<p className="text-white/55 font-light max-w-md mx-auto text-sm leading-relaxed">{c.subtitle}</p>
</FadeIn>
</div>

{/* Infinite cert scroll */}
<div className="relative overflow-hidden mb-20">
<div className="absolute left-0 top-0 bottom-0 w-20 z-10 bg-gradient-to-r from-forest to-transparent pointer-events-none" />
<div className="absolute right-0 top-0 bottom-0 w-20 z-10 bg-gradient-to-l from-forest to-transparent pointer-events-none" />
<motion.div
animate={{ x: ['0%', '-50%'] }}
transition={{ duration: 26, repeat: Infinity, ease: 'linear' }}
className="flex py-2"
style={{ width: 'max-content' }}
>
{duplicated.map((cert, i) => (
<CertCard key={i} cert={cert} lang={lang} />
))}
</motion.div>
</div>

{/* Partner logos */}
<div className="max-w-6xl mx-auto px-6">
<FadeIn className="text-center mb-10">
<p className="text-white/40 text-[10px] tracking-[0.28em] uppercase font-light">{c.partnersLabel}</p>
<p className="text-white/60 text-sm font-light mt-2">{c.partnersTitle}</p>
</FadeIn>
<FadeIn delay={0.1}>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-3">
{partners.map((p) => (
<div key={p.name} className="group bg-white/[0.06] hover:bg-white/[0.1] border border-white/[0.07] hover:border-white/[0.15] rounded-xl px-4 py-4 text-center transition-all duration-300">
<p className="text-white/70 text-xs font-light group-hover:text-white/90 transition-colors">{lang === 'zh' ? p.name : p.en}</p>
<p className="text-white/30 text-[10px] font-light mt-0.5">{lang === 'zh' ? p.en : p.name}</p>
</div>
))}
</div>
</FadeIn>

<FadeIn delay={0.2} className="mt-14">
<div className="grid grid-cols-2 md:grid-cols-4 gap-px bg-white/[0.07] rounded-2xl overflow-hidden">
{stats.map((s) => (
<div key={s.l} className="bg-white/[0.04] px-8 py-6 text-center">
<div className="text-2xl font-light text-white mb-1">{s.v}</div>
<div className="text-white/45 text-[10px] tracking-wide font-light">{s.l}</div>
</div>
))}
</div>
</FadeIn>
</div>
</section>
)
}
Loading
Loading