diff --git a/app/pages/[slug]/page.tsx b/app/pages/[slug]/page.tsx
index cdf8c45..acb73d7 100644
--- a/app/pages/[slug]/page.tsx
+++ b/app/pages/[slug]/page.tsx
@@ -10,6 +10,7 @@ import { PostLayout } from '@/components/post-layout';
import { ScrollReveal } from '@/components/scroll-reveal';
import { SectionDivider } from '@/components/section-divider';
import { JsonLd } from '@/components/json-ld';
+import { DevEnvDeviceHero } from '@/components/dev-env-device-hero';
export function generateStaticParams() {
const params = allPages.map((page) => ({
@@ -115,18 +116,22 @@ export default async function StaticPage({ params }: Props) {
data-toc-content={slug}
className="prose prose-lg prose-slate mx-auto max-w-none dark:prose-invert"
>
- {page.feature_image && (
-
-
-
+ {slug === 'dev-env' ? (
+
+ ) : (
+ page.feature_image && (
+
+
+
+ )
)}
diff --git a/components/dev-env-device-hero.tsx b/components/dev-env-device-hero.tsx
new file mode 100644
index 0000000..09eb48f
--- /dev/null
+++ b/components/dev-env-device-hero.tsx
@@ -0,0 +1,97 @@
+'use client';
+
+import { SiArchlinux, SiUbuntu, SiLinux } from 'react-icons/si';
+
+/**
+ * Mac mini + 螢幕 3D 裝置展示
+ * 使用純 CSS 3D transforms,取代開發工作環境頁的 feature_image
+ */
+export function DevEnvDeviceHero() {
+ return (
+
+
+ {/* Monitor */}
+
+ {/* Bezel */}
+
+ {/* Screen */}
+
+ {/* macOS Desktop mockup */}
+
+ {/* macOS Menu bar - 半透明毛玻璃 */}
+
+ {'\uF8FF'}
+ Terminal
+
+
+
+
+
+ 14:30
+
+
+ {/* Window - Terminal 顯示 Arch / Ubuntu / Tux 三個 Logo */}
+
+
+
+
+
+
+
+
+ $ neofetch --ascii_distro arch,ubuntu,tux
+
+
+
+
+
+
+
+ {/* Monitor stand */}
+
+
+
+ {/* Desk surface - Mac mini 與鍵盤均勻放置 */}
+
+ {/* 鍵盤 - Magic Keyboard 風格,鍵帽網格 */}
+
+
+
+ {[14, 14, 13, 12].map((keyCount, row) => (
+
+ {Array.from({ length: keyCount }).map((_, col) => (
+
+ ))}
+
+ ))}
+
+
+
+
+ {/* Mac mini M4 2024 - 頂視,避免 3D 偽影 */}
+
+
+
+
+ );
+}
diff --git a/styles/globals.css b/styles/globals.css
index b09d785..bad9d3d 100644
--- a/styles/globals.css
+++ b/styles/globals.css
@@ -56,6 +56,460 @@
100% { transform: translate3d(0,0,0) scale(1); }
}
+/* Dev env device hero - Mac mini + 螢幕 3D mockup */
+@keyframes device-float {
+ 0% { transform: perspective(800px) rotateY(-4deg) rotateX(2deg) translate3d(0, 0, 0); }
+ 50% { transform: perspective(800px) rotateY(-4deg) rotateX(2deg) translate3d(0, -8px, 0); }
+ 100% { transform: perspective(800px) rotateY(-4deg) rotateX(2deg) translate3d(0, 0, 0); }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .dev-env-device-hero .dev-env-device-scene {
+ animation: none;
+ }
+}
+
+.dev-env-device-hero {
+ min-height: 320px;
+}
+
+.dev-env-device-scene {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ perspective: 800px;
+ transform-style: preserve-3d;
+ animation: device-float 8s ease-in-out infinite;
+}
+
+.dev-env-monitor {
+ position: relative;
+ width: min(340px, 88vw);
+ padding-bottom: 75%; /* 4:3 較高,讓 terminal 內容完整顯示 */
+ transform-style: preserve-3d;
+}
+
+.dev-env-monitor + .dev-env-desk {
+ margin-top: 40px;
+}
+
+.dev-env-bezel {
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(145deg, #2d2d2d 0%, #1a1a1a 100%);
+ border-radius: 12px;
+ padding: 8px;
+ box-shadow:
+ 0 25px 50px -12px rgba(0, 0, 0, 0.4),
+ 0 0 0 1px rgba(255, 255, 255, 0.05) inset,
+ -2px -2px 8px rgba(0, 0, 0, 0.3);
+}
+
+.dark .dev-env-bezel {
+ background: linear-gradient(145deg, #1a1a1a 0%, #0d0d0d 100%);
+}
+
+.dev-env-screen {
+ position: absolute;
+ inset: 8px;
+ border-radius: 6px;
+ overflow: hidden;
+ background: linear-gradient(165deg, #e8ecf0 0%, #dde2e8 50%, #e2e6ec 100%);
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4) inset;
+}
+
+.dark .dev-env-screen {
+ background: linear-gradient(165deg, #1a1d21 0%, #15181c 50%, #1c1f24 100%);
+}
+
+/* macOS desktop mockup */
+.dev-env-desktop {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ flex-direction: column;
+}
+
+/* macOS Menu bar - 毛玻璃效果 */
+.dev-env-menubar {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ height: 26px;
+ padding: 0 14px;
+ background: rgba(255, 255, 255, 0.72);
+ backdrop-filter: blur(20px) saturate(180%);
+ -webkit-backdrop-filter: blur(20px) saturate(180%);
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
+ font-size: 13px;
+ font-weight: 500;
+ color: rgba(0, 0, 0, 0.88);
+}
+
+.dark .dev-env-menubar {
+ background: rgba(50, 50, 50, 0.72);
+ border-bottom-color: rgba(255, 255, 255, 0.06);
+ color: rgba(255, 255, 255, 0.9);
+}
+
+.dev-env-apple {
+ font-size: 17px;
+ font-weight: 400;
+ margin-right: 6px;
+ font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", sans-serif;
+}
+
+.dev-env-app-name {
+ opacity: 0.95;
+}
+
+.dev-env-spacer {
+ flex: 1;
+}
+
+.dev-env-menubar-right {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+}
+
+.dev-env-menubar-icon {
+ width: 16px;
+ height: 16px;
+ border-radius: 50%;
+ background: rgba(0, 0, 0, 0.25);
+}
+
+.dark .dev-env-menubar-icon {
+ background: rgba(255, 255, 255, 0.2);
+}
+
+.dev-env-time {
+ font-size: 12px;
+ opacity: 0.9;
+}
+
+.dev-env-window {
+ flex: 1;
+ min-height: 80px;
+ margin: 12px 16px;
+ background: rgba(255, 255, 255, 0.98);
+ border-radius: 10px;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
+ overflow: hidden;
+ border: 1px solid rgba(0, 0, 0, 0.06);
+ display: flex;
+ flex-direction: column;
+}
+
+.dark .dev-env-window {
+ background: rgba(30, 30, 30, 0.98);
+ border-color: rgba(255, 255, 255, 0.06);
+}
+
+/* macOS 紅黃綠三色按鈕 */
+.dev-env-window-titlebar {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ height: 32px;
+ padding: 0 12px;
+ background: rgba(0, 0, 0, 0.04);
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06);
+}
+
+.dark .dev-env-window-titlebar {
+ background: rgba(255, 255, 255, 0.03);
+ border-bottom-color: rgba(255, 255, 255, 0.06);
+}
+
+.dev-env-traffic-light {
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ flex-shrink: 0;
+}
+
+.dev-env-traffic-red {
+ background: #ff5f57;
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
+}
+
+.dev-env-traffic-yellow {
+ background: #febc2e;
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
+}
+
+.dev-env-traffic-green {
+ background: #28c840;
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
+}
+
+.dev-env-window-content {
+ padding: 10px 14px;
+ font-family: ui-monospace, "SF Mono", "Monaco", "Consolas", monospace;
+ font-size: 9px;
+ line-height: 1.2;
+ overflow: visible;
+ flex: 1;
+ min-height: 52px;
+ display: flex;
+ flex-direction: column;
+}
+
+.dev-env-terminal-prompt {
+ margin-bottom: 8px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ flex-shrink: 0;
+}
+
+.dev-env-terminal-logos {
+ display: flex;
+ gap: 12px;
+ justify-content: center;
+ align-items: center;
+ flex-wrap: wrap;
+ flex: 1 0 auto;
+ min-height: 36px;
+ padding: 4px 0;
+}
+
+.dev-env-logo-svg {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.dev-env-logo-svg svg {
+ flex-shrink: 0;
+ display: block;
+}
+
+.dev-env-svg-arch {
+ color: #1793d1;
+}
+
+.dark .dev-env-svg-arch {
+ color: #38bdf8;
+}
+
+.dev-env-svg-ubuntu {
+ color: #e95420;
+}
+
+.dark .dev-env-svg-ubuntu {
+ color: #f97316;
+}
+
+.dev-env-svg-tux {
+ color: #334155;
+}
+
+.dark .dev-env-svg-tux {
+ color: #cbd5e1;
+}
+
+.dev-env-terminal-line {
+ height: 8px;
+ margin-bottom: 6px;
+ background: rgba(0, 0, 0, 0.08);
+ border-radius: 2px;
+}
+
+.dev-env-terminal-line.short {
+ width: 60%;
+}
+
+.dark .dev-env-terminal-line {
+ background: rgba(255, 255, 255, 0.05);
+}
+
+.dev-env-prompt {
+ color: #22c55e;
+}
+
+.dark .dev-env-prompt {
+ color: #4ade80;
+}
+
+.dev-env-stand {
+ position: absolute;
+ bottom: -12px;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 60px;
+ height: 24px;
+ background: linear-gradient(180deg, #8a8a8a 0%, #6e6e6e 100%);
+ border-radius: 12px 12px 0 0;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
+}
+
+.dark .dev-env-stand {
+ background: linear-gradient(180deg, #2d2d2d 0%, #1a1a1a 100%);
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
+}
+
+.dev-env-desk {
+ position: relative;
+ width: min(360px, 90vw);
+ height: 56px;
+ margin: 0 auto;
+ padding: 0 12px;
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-evenly;
+ gap: 12px;
+ background: linear-gradient(180deg, #b8b8b8 0%, #9a9a9a 50%, #888888 100%);
+ border-radius: 4px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
+}
+
+.dark .dev-env-desk {
+ background: linear-gradient(180deg, #3d3d3d 0%, #2a2a2a 100%);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
+}
+
+/* 鍵盤 - Magic Keyboard 風格,鍵帽網格 */
+.dev-env-keyboard {
+ width: 110px;
+ height: 36px;
+ display: flex;
+ align-items: flex-end;
+ justify-content: center;
+}
+
+.dev-env-keyboard-body {
+ width: 100%;
+ padding: 4px 6px;
+ background: linear-gradient(
+ 180deg,
+ #e8e8ed 0%,
+ #d1d1d6 25%,
+ #c8c8cd 60%,
+ #b8b8bd 100%
+ );
+ border-radius: 6px;
+ box-shadow:
+ 0 2px 8px rgba(0, 0, 0, 0.2),
+ inset 0 1px 0 rgba(255, 255, 255, 0.6);
+}
+
+.dark .dev-env-keyboard-body {
+ background: linear-gradient(
+ 180deg,
+ #4a4a4f 0%,
+ #3d3d42 40%,
+ #2d2d32 100%
+ );
+ box-shadow:
+ 0 2px 8px rgba(0, 0, 0, 0.4),
+ inset 0 1px 0 rgba(255, 255, 255, 0.06);
+}
+
+.dev-env-keyboard-keys {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+}
+
+.dev-env-keyboard-row {
+ display: flex;
+ justify-content: center;
+ gap: 1px;
+}
+
+.dev-env-keyboard-row:nth-child(2) { padding-left: 4px; }
+.dev-env-keyboard-row:nth-child(3) { padding-left: 8px; }
+.dev-env-keyboard-row:nth-child(4) { padding-left: 12px; }
+.dev-env-keyboard-row:nth-child(5) { padding-left: 20px; }
+
+.dev-env-keyboard-row-space {
+ padding-left: 24px !important;
+}
+
+.dev-env-key-space {
+ width: 36px;
+}
+
+.dev-env-key {
+ width: 5px;
+ height: 4px;
+ background: linear-gradient(
+ 180deg,
+ rgba(255, 255, 255, 0.9) 0%,
+ rgba(0, 0, 0, 0.08) 100%
+ );
+ border-radius: 1px;
+ flex-shrink: 0;
+}
+
+.dark .dev-env-key {
+ background: linear-gradient(
+ 180deg,
+ rgba(255, 255, 255, 0.08) 0%,
+ rgba(0, 0, 0, 0.4) 100%
+ );
+}
+
+/* Mac mini - 頂視,無 3D 旋轉,避免偽影 */
+.dev-env-macmini {
+ display: flex;
+ align-items: flex-end;
+ justify-content: center;
+ height: 44px;
+}
+
+.dev-env-macmini-top {
+ width: 64px;
+ height: 64px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: linear-gradient(
+ 145deg,
+ #eaeaf0 0%,
+ #d4d4d9 25%,
+ #c4c4c9 55%,
+ #b4b4b9 85%,
+ #a8a8ad 100%
+ );
+ border-radius: 14px;
+ box-shadow:
+ 0 4px 12px rgba(0, 0, 0, 0.15),
+ inset 0 2px 4px rgba(255, 255, 255, 0.6),
+ inset 0 -1px 2px rgba(0, 0, 0, 0.06);
+}
+
+.dark .dev-env-macmini-top {
+ background: linear-gradient(
+ 145deg,
+ #7a7a7f 0%,
+ #5e5e63 25%,
+ #4e4e53 55%,
+ #3e3e43 85%,
+ #323237 100%
+ );
+ box-shadow:
+ 0 4px 12px rgba(0, 0, 0, 0.35),
+ inset 0 2px 4px rgba(255, 255, 255, 0.08),
+ inset 0 -1px 2px rgba(0, 0, 0, 0.3);
+}
+
+.dev-env-macmini-apple {
+ font-size: 24px;
+ font-weight: 400;
+ color: rgba(0, 0, 0, 0.5);
+ font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", sans-serif;
+}
+
+.dark .dev-env-macmini-apple {
+ color: rgba(255, 255, 255, 0.4);
+}
+
+
@keyframes mastodon-shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }