feat(identity): real author photo across the site + About page rewrite
Deploy / build (push) Successful in 48s
Deploy / build (push) Successful in 48s
The photo is the identity evidence the external review flagged as the biggest gap. One source image (Pictures/Professional Portrait with Blue Gradient Background.png, 1254x1254), two sizes, WebP first with a JPEG fallback — continuous tone is the case where WebP/JPEG beat PNG: 640x640 42 KB JPG / 26 KB WebP About page (190 CSS px) 128x128 3 KB JPG / 2 KB WebP author card + home hero avatar It now appears in the About hero, the author card at the end of every post, the home hero (both languages), and JSON-LD Person.image — the node search engines read to associate the name with a face. About went from 1,024 characters to five sections: identity with photo, "By the numbers" (162 containers / 3 hosts / 71 essays / 0 hosting companies between me and the servers), the real tech stack in four columns, the Hoelee Enterprise block (website design and development is the main business, email hosting secondary), and a contact split between hiring and business. Also: the headshot's dark-blue background sits close to the page background, so the photo, hero avatar and card avatar get a faint brand ring plus shadow. Pictures/ keeps the converted JPG master next to the untouched PNG.
This commit is contained in:
1 parent
215051ef08
commit
6b7c5a983f
12 files changed
+410
-65
No files matched your search
@@ -3,21 +3,62 @@ import { SITE } from '../config';
|
||||
|
||||
interface Props {
|
||||
bio?: string;
|
||||
lang?: 'en' | 'zh';
|
||||
}
|
||||
|
||||
const { bio = 'Full-stack developer & DevOps engineer. I build web apps, self-host infrastructure, and automate things — this blog is my living portfolio.' } = Astro.props;
|
||||
const {
|
||||
bio = 'Full-stack developer & DevOps engineer based in Malaysia. I run my own production infrastructure and build web systems for small businesses — this blog is the living record.',
|
||||
lang = 'en',
|
||||
} = Astro.props;
|
||||
const isZh = lang === 'zh';
|
||||
|
||||
const t = isZh
|
||||
? {
|
||||
hiring: '招聘方:技术栈与履历',
|
||||
hiringHref: '/zh/about/',
|
||||
sme: '马来西亚中小企业:网站设计与开发',
|
||||
smeHref: SITE.website,
|
||||
stack: '全部技术文章',
|
||||
stackHref: '/zh/posts/',
|
||||
}
|
||||
: {
|
||||
hiring: 'Hiring? Profile & stack',
|
||||
hiringHref: '/about/',
|
||||
sme: 'Malaysian SME? Website design & development',
|
||||
smeHref: SITE.website,
|
||||
stack: 'All posts',
|
||||
stackHref: '/posts/',
|
||||
};
|
||||
---
|
||||
|
||||
<div class="author-card">
|
||||
<div class="avatar" aria-hidden="true">{SITE.handle.charAt(0)}</div>
|
||||
<div>
|
||||
<p class="who">{SITE.author}</p>
|
||||
<picture>
|
||||
<source srcset={SITE.photo.smallWebp} type="image/webp" />
|
||||
<img
|
||||
class="avatar"
|
||||
src={SITE.photo.small}
|
||||
alt={SITE.author}
|
||||
width="64"
|
||||
height="64"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
</picture>
|
||||
<div class="author-body">
|
||||
<p class="who">{SITE.author} · {SITE.handle}</p>
|
||||
<p class="bio">{bio}</p>
|
||||
<div class="links">
|
||||
<a href={SITE.linkedin} target="_blank" rel="noopener">LinkedIn</a>
|
||||
<a href={SITE.github} target="_blank" rel="noopener">GitHub</a>
|
||||
<a href={SITE.gitea} target="_blank" rel="noopener">Gitea</a>
|
||||
<a href={SITE.website} target="_blank" rel="noopener">hoelee.com</a>
|
||||
<a href={`mailto:${SITE.email}`}>{SITE.email}</a>
|
||||
</div>
|
||||
<p class="author-split">
|
||||
<a href={t.hiringHref}>{t.hiring}</a>
|
||||
<span class="sep">·</span>
|
||||
<a href={t.smeHref} target="_blank" rel="noopener">{t.sme} ↗</a>
|
||||
<span class="sep">·</span>
|
||||
<a href={t.stackHref}>{t.stack}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -17,4 +17,14 @@ export const SITE = {
|
||||
gitea: 'https://git.hoelee.com/hoelee',
|
||||
website: 'https://www.hoelee.com',
|
||||
twitter: '', // add if you have one
|
||||
// Author photo (B4, 2026-10-16). One real headshot, two sizes, WebP first with the
|
||||
// JPEG as fallback — continuous-tone images are the case where JPEG/WebP beat PNG.
|
||||
// Used by: the About page, the author card on every post, the home hero, and the
|
||||
// JSON-LD `Person.image` (the identity signal search engines read).
|
||||
photo: {
|
||||
large: '/images/lee-teong-hoe-640.jpg', // 640x640 — About page (320 CSS px @2x)
|
||||
largeWebp: '/images/lee-teong-hoe-640.webp',
|
||||
small: '/images/lee-teong-hoe-128.jpg', // 128x128 — author card avatar (64 CSS px @2x)
|
||||
smallWebp: '/images/lee-teong-hoe-128.webp',
|
||||
},
|
||||
} as const;
|
||||
@@ -130,6 +130,7 @@ const nav = isZh
|
||||
alternateName: SITE.handle,
|
||||
url: SITE.url,
|
||||
email: SITE.email,
|
||||
image: new URL(SITE.photo.large, SITE.url).href,
|
||||
sameAs: [SITE.linkedin, SITE.github, SITE.gitea, SITE.website].filter(Boolean),
|
||||
jobTitle: 'Full-stack Developer & DevOps Engineer',
|
||||
// The consultancy that does the client work — ties the blog's author node to
|
||||
|
||||
+119
-25
@@ -6,36 +6,130 @@ import { SITE } from '../config';
|
||||
|
||||
<BaseLayout
|
||||
title="About — Mr Hoelee"
|
||||
description={`About ${SITE.author} (${SITE.handle}).`}
|
||||
description={`${SITE.author} (${SITE.handle}) — full-stack developer and DevOps engineer in Malaysia. Runs 162 containers across three self-managed hosts and builds web systems for small businesses.`}
|
||||
switchHref="/zh/about/"
|
||||
altLocaleUrl={`${SITE.url}/zh/about/`}
|
||||
>
|
||||
<h1>About</h1>
|
||||
<section class="about-hero">
|
||||
<picture>
|
||||
<source srcset={SITE.photo.largeWebp} type="image/webp" />
|
||||
<img
|
||||
class="about-photo"
|
||||
src={SITE.photo.large}
|
||||
alt={`${SITE.author} (${SITE.handle})`}
|
||||
width="320"
|
||||
height="320"
|
||||
decoding="async"
|
||||
/>
|
||||
</picture>
|
||||
<div>
|
||||
<h1>{SITE.author} <span class="handle">({SITE.handle})</span></h1>
|
||||
<p class="about-lede">
|
||||
Full-stack developer and DevOps engineer in Malaysia. I run the production
|
||||
infrastructure behind my own and my clients' systems —
|
||||
<strong>162 containers across three self-managed hosts</strong> — and I build
|
||||
web systems for Malaysian small businesses.
|
||||
</p>
|
||||
<p class="about-links">
|
||||
<a href={SITE.linkedin} target="_blank" rel="noopener">LinkedIn</a>
|
||||
<span class="sep">·</span>
|
||||
<a href={SITE.github} target="_blank" rel="noopener">GitHub</a>
|
||||
<span class="sep">·</span>
|
||||
<a href={SITE.gitea} target="_blank" rel="noopener">git.hoelee.com</a>
|
||||
<span class="sep">·</span>
|
||||
<a href={`mailto:${SITE.email}`}>{SITE.email}</a>
|
||||
</p>
|
||||
<p class="about-open">Open to <strong>remote / hybrid</strong> engineering roles.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<h2>By the numbers</h2>
|
||||
<div class="proof-grid">
|
||||
<div class="proof-card"><span class="n">162</span><span class="l">containers in production</span></div>
|
||||
<div class="proof-card"><span class="n">3</span><span class="l">self-managed hosts (78 compose stacks)</span></div>
|
||||
<div class="proof-card"><span class="n">71</span><span class="l">write-ups, each in English and 中文</span></div>
|
||||
<div class="proof-card"><span class="n">0</span><span class="l">hosting companies between me and the servers</span></div>
|
||||
</div>
|
||||
<p>
|
||||
I'm {SITE.author} ({SITE.handle}), a full-stack developer and DevOps engineer
|
||||
based in Malaysia. I build web applications — PHP/CodeIgniter, Java/Spring,
|
||||
JavaScript/TypeScript/React — and self-host a ~140-container homelab
|
||||
(Docker, Traefik, nginx, Cloudflare tunnels). I run Hoelee Enterprise, a
|
||||
website design & development business for Malaysian SMEs (email hosting is
|
||||
a side offering, not the focus).
|
||||
</p>
|
||||
<p>
|
||||
This blog is my living portfolio: I document what I build, from self-hosted
|
||||
CI/CD and Docker orchestration to Web3 experiments and AI automation. If
|
||||
you're a recruiter, a client, or a fellow builder, the posts here are the
|
||||
most honest picture of what I can do.
|
||||
That footprint is client websites, a mail platform, CI/CD, workflow automation,
|
||||
analytics and this blog — held together by hand. It is also why the posts here
|
||||
are specific: when something breaks in production, I am the one who finds out,
|
||||
and the write-up is the record of what the evidence actually said.
|
||||
</p>
|
||||
|
||||
<AuthorCard bio="Full-stack developer & DevOps engineer based in Malaysia. Open to remote / hybrid opportunities." />
|
||||
<h2>What I actually work with</h2>
|
||||
<div class="stack-grid">
|
||||
<div>
|
||||
<h3>Infrastructure & DevOps</h3>
|
||||
<ul>
|
||||
<li>Docker & Compose, Traefik, nginx, Cloudflare tunnels</li>
|
||||
<li>Gitea Actions (self-hosted CI/CD), act_runner</li>
|
||||
<li>Prometheus, Grafana, cAdvisor, Uptime Kuma</li>
|
||||
<li>Unraid, Synology DSM, Linux (Debian/Ubuntu/Alpine)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Backend & automation</h3>
|
||||
<ul>
|
||||
<li>PHP / CodeIgniter, Java / Spring</li>
|
||||
<li>Node.js / TypeScript, Python</li>
|
||||
<li>n8n, NocoDB, PostgreSQL / MySQL</li>
|
||||
<li>REST APIs, webhooks, browser automation (CDP/Playwright)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Frontend & web</h3>
|
||||
<ul>
|
||||
<li>Astro, JavaScript / TypeScript, React</li>
|
||||
<li>WordPress (Kadence, Elementor, custom plugins)</li>
|
||||
<li>Core Web Vitals, accessibility, responsive layout</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Self-hosted services I run</h3>
|
||||
<ul>
|
||||
<li>Authentik SSO, Mailcow, AList, Mem0</li>
|
||||
<li>Umami analytics, Tor onion services</li>
|
||||
<li>Whisper/TTS inference on a local GPU</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Contact</h2>
|
||||
<p>
|
||||
<a href={`mailto:${SITE.email}`}>{SITE.email}</a>
|
||||
<span class="sep">·</span>
|
||||
<a href={SITE.linkedin} target="_blank" rel="noopener">LinkedIn</a>
|
||||
<span class="sep">·</span>
|
||||
<a href={SITE.gitea} target="_blank" rel="noopener">git.hoelee.com</a>
|
||||
<span class="sep">·</span>
|
||||
<a href={SITE.website} target="_blank" rel="noopener">www.hoelee.com</a>
|
||||
</p>
|
||||
<h2>For Malaysian SMEs — Hoelee Enterprise</h2>
|
||||
<div class="sme-block">
|
||||
<p>
|
||||
<strong>Website design and development is my main business</strong> — fast,
|
||||
maintainable sites for Malaysian small and medium businesses, from the design
|
||||
through to the hosting, DNS, backups and monitoring underneath it.
|
||||
<strong>Email hosting</strong> is a secondary offering, not the focus.
|
||||
</p>
|
||||
<p>
|
||||
Because I run the infrastructure myself, you get one person who can answer for
|
||||
the whole stack instead of a developer who cannot see the server and a hosting
|
||||
company that cannot read the code.
|
||||
</p>
|
||||
<p>
|
||||
<a href={SITE.website} target="_blank" rel="noopener">www.hoelee.com</a>
|
||||
<span class="sep">·</span>
|
||||
<a href="https://wa.me/60127972969" target="_blank" rel="noopener">WhatsApp +60 12-797 2969</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2>Get in touch</h2>
|
||||
<div class="contact-split">
|
||||
<div>
|
||||
<h3>For hiring & engineering roles</h3>
|
||||
<p><a href={`mailto:${SITE.email}`}>{SITE.email}</a></p>
|
||||
<p><a href={SITE.linkedin} target="_blank" rel="noopener">LinkedIn</a> · <a href={SITE.github} target="_blank" rel="noopener">GitHub</a></p>
|
||||
<p>Remote or hybrid; available for a technical conversation any time.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3>For business & web projects</h3>
|
||||
<p><a href="https://wa.me/60127972969" target="_blank" rel="noopener">WhatsApp +60 12-797 2969</a></p>
|
||||
<p><a href={SITE.website} target="_blank" rel="noopener">www.hoelee.com</a></p>
|
||||
<p>Malaysian SMEs: website design & development, hosting and maintenance.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AuthorCard />
|
||||
</BaseLayout>
|
||||
+14
-1
@@ -25,7 +25,20 @@ const featuredPosts = featured.length === 4 ? featured : posts.slice(0, 4);
|
||||
|
||||
<BaseLayout title={SITE.title} description={SITE.description} altLocaleUrl={`${SITE.url}/zh/`}>
|
||||
<section class="hero">
|
||||
<h1>{SITE.author} — full-stack developer & DevOps engineer</h1>
|
||||
<div class="hero-id">
|
||||
<picture>
|
||||
<source srcset={SITE.photo.smallWebp} type="image/webp" />
|
||||
<img
|
||||
class="hero-photo"
|
||||
src={SITE.photo.small}
|
||||
alt={`${SITE.author} (${SITE.handle})`}
|
||||
width="72"
|
||||
height="72"
|
||||
decoding="async"
|
||||
/>
|
||||
</picture>
|
||||
<h1>{SITE.author} — full-stack developer & DevOps engineer</h1>
|
||||
</div>
|
||||
<p class="hero-sub">
|
||||
I run <strong>162 containers across three self-managed hosts</strong> — client sites, a
|
||||
mail platform, CI/CD, workflow automation and this blog — and I build web systems for
|
||||
|
||||
+113
-23
@@ -6,35 +6,125 @@ import { SITE } from '../../config';
|
||||
|
||||
<BaseLayout
|
||||
title="关于 — Mr Hoelee"
|
||||
description={`关于 ${SITE.author}(${SITE.handle})。`}
|
||||
description={`${SITE.author}(${SITE.handle})——在马来西亚的全栈开发者兼 DevOps 工程师。独自运维三台服务器上的 162 个容器,为中小企业构建 Web 系统。`}
|
||||
lang="zh"
|
||||
switchHref="/about/"
|
||||
altLocaleUrl={`${SITE.url}/about/`}
|
||||
>
|
||||
<h1>关于</h1>
|
||||
<section class="about-hero">
|
||||
<picture>
|
||||
<source srcset={SITE.photo.largeWebp} type="image/webp" />
|
||||
<img
|
||||
class="about-photo"
|
||||
src={SITE.photo.large}
|
||||
alt={`${SITE.author}(${SITE.handle})`}
|
||||
width="320"
|
||||
height="320"
|
||||
decoding="async"
|
||||
/>
|
||||
</picture>
|
||||
<div>
|
||||
<h1>{SITE.author} <span class="handle">({SITE.handle})</span></h1>
|
||||
<p class="about-lede">
|
||||
在马来西亚的全栈开发者兼 DevOps 工程师。我亲手运维自己和客户系统背后的生产环境——
|
||||
<strong>三台自管服务器上的 162 个容器</strong>——并为马来西亚中小企业构建 Web 系统。
|
||||
</p>
|
||||
<p class="about-links">
|
||||
<a href={SITE.linkedin} target="_blank" rel="noopener">LinkedIn</a>
|
||||
<span class="sep">·</span>
|
||||
<a href={SITE.github} target="_blank" rel="noopener">GitHub</a>
|
||||
<span class="sep">·</span>
|
||||
<a href={SITE.gitea} target="_blank" rel="noopener">git.hoelee.com</a>
|
||||
<span class="sep">·</span>
|
||||
<a href={`mailto:${SITE.email}`}>{SITE.email}</a>
|
||||
</p>
|
||||
<p class="about-open">接受 <strong>远程 / 混合</strong> 工程岗位机会。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<h2>几个数字</h2>
|
||||
<div class="proof-grid">
|
||||
<div class="proof-card"><span class="n">162</span><span class="l">个生产环境容器</span></div>
|
||||
<div class="proof-card"><span class="n">3</span><span class="l">台自管服务器(78 个 compose 栈)</span></div>
|
||||
<div class="proof-card"><span class="n">71</span><span class="l">篇长文,每篇中英双版</span></div>
|
||||
<div class="proof-card"><span class="n">0</span><span class="l">夹在我和服务器之间的托管商</span></div>
|
||||
</div>
|
||||
<p>
|
||||
我是 {SITE.author}({SITE.handle}),一名base在马来西亚的全栈开发者兼 DevOps
|
||||
工程师。我做 Web 应用——PHP/CodeIgniter、Java/Spring、JavaScript/TypeScript/React——
|
||||
并自托管一个约 140 个容器的 homelab(Docker、Traefik、nginx、Cloudflare tunnel)。
|
||||
我运营 Hoelee Enterprise,一家面向马来西亚中小企业的网站设计开发公司
|
||||
(邮箱托管只是副业,并非主业)。
|
||||
</p>
|
||||
<p>
|
||||
这个博客就是我的动态作品集:我把自己构建的东西记录下来,从自托管 CI/CD、
|
||||
Docker 编排,到 Web3 实验与 AI 自动化。无论你是招聘方、客户,还是同路的
|
||||
开发者,这里的文章都是我能做什么最诚实的呈现。
|
||||
这套环境里有客户网站、邮件平台、CI/CD、流程自动化、统计和这个博客,全靠手工拴在一起。
|
||||
这也是为什么这里的文章都很具体:生产环境出问题时,第一个知道的人是我,
|
||||
文章就是「证据到底说了什么」的记录。
|
||||
</p>
|
||||
|
||||
<AuthorCard bio="base在马来西亚的全栈开发者兼 DevOps 工程师。接受远程/混合办公机会。" />
|
||||
<h2>我实际在用的东西</h2>
|
||||
<div class="stack-grid">
|
||||
<div>
|
||||
<h3>基建与 DevOps</h3>
|
||||
<ul>
|
||||
<li>Docker 与 Compose、Traefik、nginx、Cloudflare tunnel</li>
|
||||
<li>Gitea Actions(自托管 CI/CD)、act_runner</li>
|
||||
<li>Prometheus、Grafana、cAdvisor、Uptime Kuma</li>
|
||||
<li>Unraid、Synology DSM、Linux(Debian/Ubuntu/Alpine)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3>后端与自动化</h3>
|
||||
<ul>
|
||||
<li>PHP / CodeIgniter、Java / Spring</li>
|
||||
<li>Node.js / TypeScript、Python</li>
|
||||
<li>n8n、NocoDB、PostgreSQL / MySQL</li>
|
||||
<li>REST API、webhook、浏览器自动化(CDP/Playwright)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3>前端与 Web</h3>
|
||||
<ul>
|
||||
<li>Astro、JavaScript / TypeScript、React</li>
|
||||
<li>WordPress(Kadence、Elementor、自写插件)</li>
|
||||
<li>Core Web Vitals、可访问性、响应式布局</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3>我自托管的服务</h3>
|
||||
<ul>
|
||||
<li>Authentik SSO、Mailcow、AList、Mem0</li>
|
||||
<li>Umami 统计、Tor onion service</li>
|
||||
<li>本地 GPU 上的 Whisper / TTS 推理</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>联系方式</h2>
|
||||
<p>
|
||||
<a href={`mailto:${SITE.email}`}>{SITE.email}</a>
|
||||
<span class="sep">·</span>
|
||||
<a href={SITE.linkedin} target="_blank" rel="noopener">LinkedIn</a>
|
||||
<span class="sep">·</span>
|
||||
<a href={SITE.gitea} target="_blank" rel="noopener">git.hoelee.com</a>
|
||||
<span class="sep">·</span>
|
||||
<a href={SITE.website} target="_blank" rel="noopener">www.hoelee.com</a>
|
||||
</p>
|
||||
<h2>面向马来西亚中小企业 —— Hoelee Enterprise</h2>
|
||||
<div class="sme-block">
|
||||
<p>
|
||||
<strong>网站设计与开发是我的主业</strong>——为马来西亚中小企业做快速、好维护的网站,
|
||||
从设计一直到它底下的托管、DNS、备份与监控。<strong>邮箱托管是次要副业</strong>,并非主业。
|
||||
</p>
|
||||
<p>
|
||||
因为基建是我自己运维的,你面对的是一个人而不是两方——不会出现「写代码的人看不到服务器、
|
||||
托管商读不懂代码」的情况。
|
||||
</p>
|
||||
<p>
|
||||
<a href={SITE.website} target="_blank" rel="noopener">www.hoelee.com</a>
|
||||
<span class="sep">·</span>
|
||||
<a href="https://wa.me/60127972969" target="_blank" rel="noopener">WhatsApp +60 12-797 2969</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2>联系</h2>
|
||||
<div class="contact-split">
|
||||
<div>
|
||||
<h3>招聘 / 工程岗位</h3>
|
||||
<p><a href={`mailto:${SITE.email}`}>{SITE.email}</a></p>
|
||||
<p><a href={SITE.linkedin} target="_blank" rel="noopener">LinkedIn</a> · <a href={SITE.github} target="_blank" rel="noopener">GitHub</a></p>
|
||||
<p>远程或混合;随时可以聊技术。</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3>商业项目 / 网站</h3>
|
||||
<p><a href="https://wa.me/60127972969" target="_blank" rel="noopener">WhatsApp +60 12-797 2969</a></p>
|
||||
<p><a href={SITE.website} target="_blank" rel="noopener">www.hoelee.com</a></p>
|
||||
<p>马来西亚中小企业:网站设计开发、托管与维护。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AuthorCard lang="zh" bio="base 在马来西亚的全栈开发者兼 DevOps 工程师。接受远程/混合办公机会。" />
|
||||
</BaseLayout>
|
||||
@@ -29,7 +29,20 @@ const featuredPosts = featured.length === 4 ? featured : posts.slice(0, 4);
|
||||
altLocaleUrl={`${SITE.url}/`}
|
||||
>
|
||||
<section class="hero">
|
||||
<h1>{SITE.author} —— 全栈开发者兼 DevOps 工程师</h1>
|
||||
<div class="hero-id">
|
||||
<picture>
|
||||
<source srcset={SITE.photo.smallWebp} type="image/webp" />
|
||||
<img
|
||||
class="hero-photo"
|
||||
src={SITE.photo.small}
|
||||
alt={`${SITE.author}(${SITE.handle})`}
|
||||
width="72"
|
||||
height="72"
|
||||
decoding="async"
|
||||
/>
|
||||
</picture>
|
||||
<h1>{SITE.author} —— 全栈开发者兼 DevOps 工程师</h1>
|
||||
</div>
|
||||
<p class="hero-sub">
|
||||
我独自运维 <strong>三台自管服务器上的 162 个容器</strong>(客户站点、邮件平台、
|
||||
CI/CD、流程自动化与这个博客),并为马来西亚中小企业构建 Web 系统。这里写的是
|
||||
|
||||
+93
-10
@@ -284,20 +284,88 @@ pre code {
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
.author-card .avatar {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
background: var(--brand);
|
||||
color: #fff;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-weight: 700;
|
||||
font-size: 1.3rem;
|
||||
object-fit: cover;
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand) 25%, transparent);
|
||||
flex-shrink: 0;
|
||||
display: block;
|
||||
}
|
||||
.author-card .author-body { min-width: 0; }
|
||||
.author-card .who { font-weight: 600; margin: 0; }
|
||||
.author-card .bio { color: var(--muted); font-size: 0.9rem; margin: 0.25rem 0 0.5rem; }
|
||||
.author-card .links { display: flex; gap: 0.75rem; font-size: 0.85rem; }
|
||||
.author-card .links { display: flex; gap: 0.75rem; font-size: 0.85rem; flex-wrap: wrap; }
|
||||
.author-card .author-split { font-size: 0.82rem; color: var(--muted); margin: 0.6rem 0 0; }
|
||||
.author-card .author-split a { color: var(--muted); }
|
||||
.author-card .author-split a:hover { color: var(--brand); }
|
||||
|
||||
/* ---------- About ---------- */
|
||||
.about-hero {
|
||||
display: flex;
|
||||
gap: 1.75rem;
|
||||
align-items: flex-start;
|
||||
margin: 0.5rem 0 2.25rem;
|
||||
padding-bottom: 1.75rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.about-photo {
|
||||
width: 190px;
|
||||
height: 190px;
|
||||
border-radius: var(--radius);
|
||||
border: 1px solid var(--border);
|
||||
/* The headshot's own dark-blue background sits close to the page background, so a
|
||||
faint brand ring + shadow is what actually separates it from the UI. */
|
||||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand) 30%, transparent),
|
||||
0 10px 28px rgba(0, 0, 0, 0.35);
|
||||
object-fit: cover;
|
||||
flex-shrink: 0;
|
||||
display: block;
|
||||
}
|
||||
.about-hero h1 { margin-bottom: 0.6rem; }
|
||||
.about-hero h1 .handle { color: var(--muted); font-weight: 500; }
|
||||
.about-lede { font-size: 1.02rem; color: var(--muted); margin-bottom: 0.75rem; }
|
||||
.about-links { font-size: 0.9rem; margin-bottom: 0.5rem; }
|
||||
.about-open { font-size: 0.9rem; color: var(--muted); margin: 0; }
|
||||
|
||||
.proof-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0.85rem;
|
||||
margin: 1rem 0 1.25rem;
|
||||
}
|
||||
.proof-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 0.85rem 0.9rem;
|
||||
}
|
||||
.proof-card .n { display: block; font-size: 1.45rem; font-weight: 700; line-height: 1.15; }
|
||||
.proof-card .l { display: block; font-size: 0.78rem; color: var(--muted); margin-top: 0.25rem; }
|
||||
|
||||
.stack-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 1rem;
|
||||
margin: 1rem 0 1.5rem;
|
||||
}
|
||||
.stack-grid h3 { font-size: 0.95rem; margin: 0 0 0.4rem; }
|
||||
.stack-grid ul { margin: 0; padding-left: 1.1rem; }
|
||||
.stack-grid li { font-size: 0.88rem; color: var(--muted); margin: 0.15rem 0; }
|
||||
|
||||
.sme-block {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-left: 3px solid var(--brand);
|
||||
border-radius: var(--radius);
|
||||
padding: 1rem 1.1rem;
|
||||
margin: 1rem 0 1.5rem;
|
||||
}
|
||||
.sme-block p { margin: 0.4rem 0; }
|
||||
.contact-split { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.25rem; margin-top: 1rem; }
|
||||
.contact-split h3 { font-size: 0.95rem; margin: 0 0 0.35rem; }
|
||||
.contact-split p { margin: 0.2rem 0; font-size: 0.9rem; color: var(--muted); }
|
||||
|
||||
/* ---------- Related posts ---------- */
|
||||
.related { margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--border); }
|
||||
@@ -346,7 +414,18 @@ pre code {
|
||||
padding-bottom: 1.5rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.hero h1 { margin-bottom: 0.75rem; }
|
||||
.hero-id { display: flex; align-items: center; gap: 1rem; margin-bottom: 0.75rem; }
|
||||
.hero-photo {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand) 30%, transparent);
|
||||
object-fit: cover;
|
||||
flex-shrink: 0;
|
||||
display: block;
|
||||
}
|
||||
.hero h1 { margin: 0; }
|
||||
.hero-sub { font-size: 1.05rem; color: var(--muted); max-width: 62ch; }
|
||||
.hero-cta {
|
||||
display: flex;
|
||||
@@ -642,6 +721,10 @@ a.category-card:hover { border-color: var(--brand); }
|
||||
h2 { font-size: 1.25rem; }
|
||||
.related-grid { grid-template-columns: 1fr; }
|
||||
.featured-grid { grid-template-columns: 1fr; }
|
||||
.proof-grid { grid-template-columns: repeat(2, 1fr); }
|
||||
.stack-grid, .contact-split { grid-template-columns: 1fr; }
|
||||
.about-hero { flex-direction: column; gap: 1rem; }
|
||||
.about-photo { width: 140px; height: 140px; }
|
||||
.search-hint, .search-key { display: none; }
|
||||
.author-card { flex-direction: column; align-items: flex-start; }
|
||||
}
|
||||
Reference in new issue
Block a user