Files
havline-native/app-src/docs/havline-logo-preview.html
T

1057 lines
31 KiB
HTML
Raw Normal View History

<!doctype html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="dark light">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3C/svg%3E">
<title>Havline Logo 概念预览</title>
<style>
:root {
--page-bg: #06121e;
--panel: #0b1c2b;
--panel-2: #0f2638;
--text: #f2fbfa;
--muted: #90aeb9;
--line: rgba(235, 255, 252, 0.12);
--line-strong: rgba(235, 255, 252, 0.22);
--brand: #2fa9ff;
--brand-2: #63f0d0;
--signal: #ffb15a;
--danger: #ff7d8d;
--stage-bg: #071a29;
--code-bg: #04101a;
--shadow: 0 24px 70px rgba(0, 0, 0, 0.28);
--radius: 8px;
--font: "Segoe UI", "Microsoft YaHei", "PingFang SC", system-ui, sans-serif;
--mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
}
html[data-theme="light"] {
--page-bg: #edf4f5;
--panel: #ffffff;
--panel-2: #e6f0f1;
--text: #0a2636;
--muted: #58717b;
--line: rgba(10, 38, 54, 0.12);
--line-strong: rgba(10, 38, 54, 0.2);
--stage-bg: #e4eff1;
--code-bg: #0a1c29;
--shadow: 0 24px 70px rgba(16, 48, 64, 0.14);
}
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
background: var(--page-bg);
color: var(--text);
font-family: var(--font);
line-height: 1.6;
}
button,
input {
font: inherit;
}
button {
cursor: pointer;
}
a {
color: inherit;
}
code,
pre {
font-family: var(--mono);
}
.sprite {
position: absolute;
width: 0;
height: 0;
overflow: hidden;
}
.topbar {
position: sticky;
top: 0;
z-index: 10;
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
min-height: 70px;
padding: 14px clamp(18px, 4vw, 56px);
background: color-mix(in srgb, var(--page-bg) 92%, transparent);
border-bottom: 1px solid var(--line);
backdrop-filter: blur(18px);
}
.topbar__brand,
.topbar__actions,
.inline-row {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.topbar__mark {
width: 34px;
height: 34px;
flex: 0 0 auto;
}
.topbar__title {
display: grid;
line-height: 1.15;
}
.topbar__title strong {
font-size: 17px;
letter-spacing: 0.01em;
}
.topbar__title span {
color: var(--muted);
font-size: 11px;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.control {
min-height: 38px;
padding: 0 14px;
color: var(--text);
background: transparent;
border: 1px solid var(--line-strong);
border-radius: var(--radius);
transition: background 160ms ease, border-color 160ms ease, color 160ms ease;
}
.control:hover,
.control[aria-pressed="true"] {
background: var(--panel-2);
border-color: var(--line-strong);
}
.control--primary {
color: #04131d;
background: linear-gradient(135deg, var(--brand), var(--brand-2));
border-color: transparent;
font-weight: 700;
}
main {
display: block;
overflow: hidden;
}
.wrap {
width: min(1180px, calc(100% - 36px));
margin: 0 auto;
}
.hero {
display: grid;
grid-template-columns: minmax(0, 0.9fr) minmax(360px, 1.1fr);
gap: clamp(28px, 5vw, 72px);
align-items: center;
min-height: calc(100vh - 70px);
padding: 72px 0;
}
.eyebrow {
margin: 0 0 14px;
color: var(--brand-2);
font-family: var(--mono);
font-size: 12px;
letter-spacing: 0.16em;
text-transform: uppercase;
}
h1,
h2,
h3,
p {
margin-top: 0;
}
h1 {
margin-bottom: 12px;
font-size: clamp(52px, 8vw, 92px);
line-height: 0.94;
letter-spacing: -0.02em;
}
.lead {
margin-bottom: 18px;
font-size: clamp(20px, 2.3vw, 30px);
font-weight: 600;
line-height: 1.25;
}
.copy {
max-width: 620px;
margin-bottom: 24px;
color: var(--muted);
font-size: 15px;
}
.hero__note {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 7px 10px;
color: var(--brand-2);
background: color-mix(in srgb, var(--brand-2) 12%, transparent);
border: 1px solid color-mix(in srgb, var(--brand-2) 28%, transparent);
border-radius: 999px;
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.08em;
}
.stage {
position: relative;
display: grid;
place-items: center;
min-height: 560px;
padding: 48px;
overflow: hidden;
background:
linear-gradient(to right, color-mix(in srgb, var(--line) 58%, transparent) 1px, transparent 1px) 0 0 / 36px 36px,
linear-gradient(to bottom, color-mix(in srgb, var(--line) 58%, transparent) 1px, transparent 1px) 0 0 / 36px 36px,
var(--stage-bg);
border: 1px solid var(--line);
border-radius: var(--radius);
box-shadow: var(--shadow);
}
.stage::before {
content: "";
position: absolute;
inset: 18px;
border: 1px solid color-mix(in srgb, var(--brand-2) 16%, transparent);
border-radius: var(--radius);
pointer-events: none;
}
.hero-lockup {
position: relative;
z-index: 1;
width: min(100%, 560px);
color: var(--text);
}
.hero-lockup__meta {
margin-top: 6px;
color: var(--muted);
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.12em;
text-align: right;
}
.section {
padding: 84px 0;
border-top: 1px solid var(--line);
}
.section__head {
display: grid;
grid-template-columns: minmax(0, 0.7fr) minmax(0, 1.3fr);
gap: 28px;
margin-bottom: 32px;
}
.section__head h2 {
margin-bottom: 0;
font-size: clamp(27px, 4vw, 44px);
line-height: 1.08;
letter-spacing: -0.01em;
}
.section__head p {
max-width: 680px;
margin-bottom: 0;
color: var(--muted);
}
.idea-grid,
.variant-grid,
.palette-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 16px;
}
.idea-card,
.variant-card,
.size-card,
.palette-card,
.lockup-card {
background: var(--panel);
border: 1px solid var(--line);
border-radius: var(--radius);
}
.idea-card {
min-height: 220px;
padding: 22px;
}
.idea-card__index {
display: block;
margin-bottom: 42px;
color: var(--brand-2);
font-family: var(--mono);
font-size: 12px;
letter-spacing: 0.12em;
}
.idea-card h3 {
margin-bottom: 8px;
font-size: 18px;
}
.idea-card p {
margin-bottom: 0;
color: var(--muted);
font-size: 14px;
}
.anatomy {
display: grid;
grid-template-columns: minmax(320px, 0.95fr) minmax(0, 1.05fr);
gap: 28px;
align-items: stretch;
}
.anatomy__visual {
display: grid;
place-items: center;
min-height: 540px;
padding: 34px;
background: #04121f;
border: 1px solid var(--line);
border-radius: var(--radius);
}
.anatomy__mark {
width: min(70%, 360px);
height: auto;
}
.anatomy__list {
display: grid;
gap: 12px;
}
.anatomy__item {
display: grid;
grid-template-columns: 44px 1fr;
gap: 16px;
align-items: start;
padding: 18px;
background: var(--panel);
border: 1px solid var(--line);
border-radius: var(--radius);
}
.anatomy__num {
display: grid;
place-items: center;
width: 36px;
height: 36px;
color: #04131d;
background: var(--brand-2);
border-radius: 50%;
font-family: var(--mono);
font-size: 12px;
font-weight: 700;
}
.anatomy__item h3 {
margin-bottom: 4px;
font-size: 16px;
}
.anatomy__item p {
margin-bottom: 0;
color: var(--muted);
font-size: 14px;
}
.variant-card {
display: grid;
place-items: center;
min-height: 250px;
padding: 26px;
}
.variant-card--light {
color: #082333;
background: #f4fbfb;
border-color: rgba(8, 35, 51, 0.12);
}
.variant-card--mono {
color: var(--text);
background: transparent;
}
.variant-card__label {
align-self: end;
width: 100%;
margin-top: 18px;
color: inherit;
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.1em;
opacity: 0.62;
text-align: center;
text-transform: uppercase;
}
.mark-64 {
width: 96px;
height: 96px;
}
.mark-32 {
width: 48px;
height: 48px;
}
.mark-24 {
width: 36px;
height: 36px;
}
.mark-16 {
width: 24px;
height: 24px;
}
.size-lab {
display: grid;
grid-template-columns: minmax(0, 1.1fr) minmax(320px, 0.9fr);
gap: 24px;
align-items: stretch;
}
.size-card {
display: grid;
place-items: center;
min-height: 350px;
padding: 32px;
overflow: hidden;
}
.size-card__figure {
display: grid;
place-items: center;
width: var(--preview-size, 96px);
height: var(--preview-size, 96px);
min-width: 20px;
min-height: 20px;
}
.size-card__figure svg {
width: 100%;
height: 100%;
}
.size-controls {
display: grid;
align-content: center;
gap: 20px;
padding: 30px;
background: var(--panel);
border: 1px solid var(--line);
border-radius: var(--radius);
}
.size-controls label {
display: grid;
gap: 10px;
color: var(--muted);
font-size: 14px;
}
input[type="range"] {
width: 100%;
accent-color: var(--brand-2);
}
.size-readout {
color: var(--text);
font-family: var(--mono);
font-size: 13px;
}
.palette-grid {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.palette-card {
min-height: 194px;
overflow: hidden;
}
.palette-card__swatch {
height: 104px;
border-bottom: 1px solid var(--line);
}
.palette-card__body {
padding: 14px;
}
.palette-card__body strong,
.palette-card__body span {
display: block;
}
.palette-card__body strong {
margin-bottom: 3px;
font-size: 14px;
}
.palette-card__body span {
color: var(--muted);
font-family: var(--mono);
font-size: 11px;
}
.lockup-grid {
display: grid;
grid-template-columns: minmax(0, 1.25fr) minmax(300px, 0.75fr);
gap: 16px;
}
.lockup-card {
display: grid;
align-content: center;
min-height: 250px;
padding: 30px;
}
.lockup-card--light {
color: #092638;
background: #f4fbfb;
border-color: rgba(9, 38, 56, 0.12);
}
.lockup-svg {
width: 100%;
height: auto;
}
.lockup-compact {
display: flex;
align-items: center;
justify-content: center;
gap: 14px;
}
.lockup-compact svg {
width: 64px;
height: 64px;
flex: 0 0 auto;
}
.lockup-compact__word {
font-size: clamp(24px, 4vw, 38px);
font-weight: 700;
letter-spacing: 0.01em;
}
.code-shell {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 12px;
align-items: start;
padding: 18px;
background: var(--code-bg);
border: 1px solid var(--line);
border-radius: var(--radius);
overflow: hidden;
}
pre {
max-height: 420px;
margin: 0;
overflow: auto;
color: #b8dce7;
font-size: 12px;
line-height: 1.6;
white-space: pre;
}
.footnote {
padding: 34px 0 56px;
color: var(--muted);
font-size: 13px;
}
@media (max-width: 980px) {
.hero,
.section__head,
.anatomy,
.size-lab,
.lockup-grid {
grid-template-columns: 1fr;
}
.hero {
min-height: auto;
padding-top: 48px;
}
.stage {
min-height: 460px;
}
.idea-grid,
.variant-grid,
.palette-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 620px) {
.topbar {
align-items: flex-start;
flex-direction: column;
}
.topbar__actions {
width: 100%;
}
.control {
flex: 1 1 auto;
}
.stage {
min-height: 360px;
padding: 28px;
}
.idea-grid,
.variant-grid,
.palette-grid {
grid-template-columns: 1fr;
}
.code-shell {
grid-template-columns: 1fr;
}
.section {
padding: 62px 0;
}
}
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
.control {
transition: none;
}
}
</style>
</head>
<body>
<svg class="sprite" aria-hidden="true" focusable="false">
<defs>
<linearGradient id="havline-bg" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#0e4b68"></stop>
<stop offset="1" stop-color="#061827"></stop>
</linearGradient>
<linearGradient id="havline-line" x1="0" y1="1" x2="1" y2="0">
<stop offset="0" stop-color="#2fa9ff"></stop>
<stop offset="1" stop-color="#63f0d0"></stop>
</linearGradient>
<symbol id="havline-mark" viewBox="0 0 96 96">
<rect x="4" y="4" width="88" height="88" rx="22" fill="url(#havline-bg)"></rect>
<rect x="4.8" y="4.8" width="86.4" height="86.4" rx="21.2" fill="none" stroke="rgba(242,255,252,.13)" stroke-width="1.6"></rect>
<path d="M31 25.5V70.5" stroke="#f2fffc" stroke-width="11" stroke-linecap="round"></path>
<path d="M65 25.5V70.5" stroke="#f2fffc" stroke-width="11" stroke-linecap="round"></path>
<path d="M31 59C39 59 42.5 49 49.5 49C56.5 49 57.5 37 65 37" stroke="url(#havline-line)" stroke-width="7" fill="none" stroke-linecap="round" stroke-linejoin="round"></path>
<circle cx="31" cy="59" r="5" fill="#2fa9ff" stroke="#061827" stroke-width="2.2"></circle>
<circle cx="65" cy="37" r="5" fill="#63f0d0" stroke="#061827" stroke-width="2.2"></circle>
</symbol>
<symbol id="havline-mono" viewBox="0 0 96 96">
<rect x="5" y="5" width="86" height="86" rx="21" fill="none" stroke="currentColor" stroke-width="5"></rect>
<path d="M31 26V70" stroke="currentColor" stroke-width="10" stroke-linecap="round"></path>
<path d="M65 26V70" stroke="currentColor" stroke-width="10" stroke-linecap="round"></path>
<path d="M31 59C39 59 42.5 49 49.5 49C56.5 49 57.5 37 65 37" stroke="currentColor" stroke-width="7" fill="none" stroke-linecap="round" stroke-linejoin="round"></path>
<circle cx="31" cy="59" r="5" fill="currentColor"></circle>
<circle cx="65" cy="37" r="5" fill="currentColor"></circle>
</symbol>
</defs>
</svg>
<header class="topbar">
<div class="topbar__brand">
<svg class="topbar__mark" viewBox="0 0 96 96" role="img" aria-label="Havline 标志">
<use href="#havline-mark"></use>
</svg>
<div class="topbar__title">
<strong>Havline</strong>
<span>Logo concept / vector study</span>
</div>
</div>
<div class="topbar__actions">
<button class="control" type="button" data-theme-set="dark" aria-pressed="true">深海</button>
<button class="control" type="button" data-theme-set="light" aria-pressed="false">白昼</button>
<button class="control control--primary" type="button" id="download-mark">下载 SVG</button>
</div>
</header>
<main>
<section class="wrap hero">
<div>
<p class="eyebrow">Brand mark / 01</p>
<h1>Havline</h1>
<p class="lead">让每条服务,安全抵达。</p>
<p class="copy">Havline 由 Haven(安全港湾)与 Line(连续链路)组成。标志把字母 H、港湾立柱与一条穿行线路叠成同一个符号,在 16px 下也能保留识别性。</p>
<div class="inline-row">
<span class="hero__note">HAV-LINE</span>
<span class="hero__note">SAFE HARBOR + ROUTE</span>
</div>
</div>
<div class="stage">
<div class="hero-lockup">
<svg class="lockup-svg" viewBox="0 0 560 136" role="img" aria-labelledby="hero-lockup-title hero-lockup-desc">
<title id="hero-lockup-title">Havline 横版标志</title>
<desc id="hero-lockup-desc">左侧为 Havline 图形标志,右侧为 havline 英文名称与标语。</desc>
<use href="#havline-mark" x="0" y="10" width="116" height="116"></use>
<text x="140" y="65" fill="currentColor" font-family="Segoe UI, Microsoft YaHei, sans-serif" font-size="48" font-weight="700" letter-spacing="0.4">havline</text>
<text x="142" y="92" fill="currentColor" font-family="SFMono-Regular, Consolas, monospace" font-size="12" font-weight="400" letter-spacing="1.5" opacity="0.62">SAFE LINE FOR EVERY SERVICE</text>
<line x1="142" y1="108" x2="522" y2="108" stroke="currentColor" stroke-width="1" opacity="0.16"></line>
</svg>
<div class="hero-lockup__meta">VECTOR IDENTITY / 2026</div>
</div>
</div>
</section>
<section class="section">
<div class="wrap">
<div class="section__head">
<h2>设计理念</h2>
<p>品牌不强调“翻墙”或“穿透”的对抗感,而强调安全、可靠和持续连接。标志中的所有元素都指向同一个动作:服务从内网出发,沿着一条清晰的线路,安全抵达外部入口。</p>
</div>
<div class="idea-grid">
<article class="idea-card">
<span class="idea-card__index">01 / HAVEN</span>
<h3>安全港湾</h3>
<p>左右两根立柱构成港湾的堤岸,也构成字母 H。它代表服务始终有一个稳定、可信的原点。</p>
</article>
<article class="idea-card">
<span class="idea-card__index">02 / LINE</span>
<h3>连续链路</h3>
<p>蓝色到青色的曲线横穿 H,把内网服务与公网入口连成一条连续的线路,暗示隧道、反代和远程访问。</p>
</article>
<article class="idea-card">
<span class="idea-card__index">03 / NODE</span>
<h3>服务节点</h3>
<p>线路两端的圆点分别代表服务和 Agent。节点让标志不仅仅是字母,更是一个可扩展的系统图。</p>
</article>
<article class="idea-card">
<span class="idea-card__index">04 / SCALE</span>
<h3>小尺寸可读</h3>
<p>主轮廓只有两根竖线、一条曲线和两个节点,避免复杂图形在 favicon、状态栏和移动端缩小时糊成一团。</p>
</article>
</div>
</div>
</section>
<section class="section">
<div class="wrap">
<div class="section__head">
<h2>标志构成</h2>
<p>这个图形同时读取为 H、港湾和线路。第一眼是品牌字母,第二眼看到的是服务从 A 点到 B 点的路径。</p>
</div>
<div class="anatomy">
<div class="anatomy__visual">
<svg class="anatomy__mark" viewBox="0 0 96 96" role="img" aria-label="Havline 标志结构">
<use href="#havline-mark"></use>
</svg>
</div>
<div class="anatomy__list">
<article class="anatomy__item">
<span class="anatomy__num">A</span>
<div>
<h3>H 的双柱</h3>
<p>两根竖向立柱保持等宽和等距,形成稳定的字母骨架。圆角端点让技术图形不显得生硬。</p>
</div>
</article>
<article class="anatomy__item">
<span class="anatomy__num">B</span>
<div>
<h3>一条服务线</h3>
<p>曲线从左侧立柱出发,在中部抬升,再从右侧立柱穿出。它不是装饰线,而是整个标志的叙事核心。</p>
</div>
</article>
<article class="anatomy__item">
<span class="anatomy__num">C</span>
<div>
<h3>两端的节点</h3>
<p>起点和终点使用不同深浅的蓝色与青色,分别对应内网服务和公网入口,也方便后续扩展状态点。</p>
</div>
</article>
<article class="anatomy__item">
<span class="anatomy__num">D</span>
<div>
<h3>圆角容器</h3>
<p>深海底板提供高对比背景,适合应用图标、登录页、侧栏品牌区和公开状态页。</p>
</div>
</article>
</div>
</div>
</div>
</section>
<section class="section">
<div class="wrap">
<div class="section__head">
<h2>版本与背景</h2>
<p>主版本适合深色 UI;浅色版本用于文档、邮件和打印;单色版本用于水印、丝印和不支持渐变的场景。</p>
</div>
<div class="variant-grid">
<article class="variant-card">
<svg class="mark-64" viewBox="0 0 96 96" role="img" aria-label="Havline 主标志">
<use href="#havline-mark"></use>
</svg>
<span class="variant-card__label">Primary / deep sea</span>
</article>
<article class="variant-card variant-card--light">
<svg class="mark-64" viewBox="0 0 96 96" role="img" aria-label="Havline 浅色背景标志">
<use href="#havline-mark"></use>
</svg>
<span class="variant-card__label">Surface / light</span>
</article>
<article class="variant-card variant-card--mono">
<svg class="mark-64" viewBox="0 0 96 96" role="img" aria-label="Havline 单色标志">
<use href="#havline-mono"></use>
</svg>
<span class="variant-card__label">Monochrome</span>
</article>
<article class="variant-card">
<svg class="mark-64" viewBox="0 0 96 96" role="img" aria-label="Havline 应用图标">
<use href="#havline-mark"></use>
</svg>
<span class="variant-card__label">App icon</span>
</article>
</div>
</div>
</section>
<section class="section">
<div class="wrap">
<div class="section__head">
<h2>尺寸实验室</h2>
<p>标志的核心结构在 16px 仍然保持 H 与线路的轮廓。拖动滑杆可以实时检查缩放后的清晰度。</p>
</div>
<div class="size-lab">
<div class="size-card">
<div class="size-card__figure" id="size-figure">
<svg viewBox="0 0 96 96" role="img" aria-label="Havline 缩放预览">
<use href="#havline-mark"></use>
</svg>
</div>
</div>
<div class="size-controls">
<label for="mark-size">
预览尺寸
<input id="mark-size" type="range" min="16" max="160" value="96" step="8">
</label>
<output class="size-readout" id="size-readout" for="mark-size">96 px</output>
<p class="copy" style="margin:0;color:var(--muted);font-size:14px;">建议最小使用尺寸:UI 图标 16px,应用图标 24px,侧栏品牌区 32px,登录页主标志 96px 以上。</p>
</div>
</div>
</div>
</section>
<section class="section">
<div class="wrap">
<div class="section__head">
<h2>色彩系统</h2>
<p>深海蓝负责安全与稳定,潮汐蓝和青色负责线路与连接,港湾白负责结构。琥珀色只作为状态或警告的补充,不进入主标志。</p>
</div>
<div class="palette-grid">
<article class="palette-card">
<div class="palette-card__swatch" style="background:#061827"></div>
<div class="palette-card__body"><strong>深海</strong><span>#061827</span></div>
</article>
<article class="palette-card">
<div class="palette-card__swatch" style="background:#0e4b68"></div>
<div class="palette-card__body"><strong>港湾蓝</strong><span>#0E4B68</span></div>
</article>
<article class="palette-card">
<div class="palette-card__swatch" style="background:#2fa9ff"></div>
<div class="palette-card__body"><strong>潮汐蓝</strong><span>#2FA9FF</span></div>
</article>
<article class="palette-card">
<div class="palette-card__swatch" style="background:#63f0d0"></div>
<div class="palette-card__body"><strong>线路青</strong><span>#63F0D0</span></div>
</article>
<article class="palette-card">
<div class="palette-card__swatch" style="background:#f2fffc"></div>
<div class="palette-card__body"><strong>港湾白</strong><span>#F2FFFC</span></div>
</article>
</div>
</div>
</section>
<section class="section">
<div class="wrap">
<div class="section__head">
<h2>横向组合</h2>
<p>横版标志适合登录页、导航栏和文档页眉。紧凑版只保留图形与名称,适合宽度受限的移动端和侧栏。</p>
</div>
<div class="lockup-grid">
<article class="lockup-card">
<svg class="lockup-svg" viewBox="0 0 560 136" role="img" aria-labelledby="lockup-title lockup-desc">
<title id="lockup-title">Havline 横向标志</title>
<desc id="lockup-desc">Havline 图形标志与完整英文标语。</desc>
<use href="#havline-mark" x="0" y="10" width="116" height="116"></use>
<text x="140" y="65" fill="currentColor" font-family="Segoe UI, Microsoft YaHei, sans-serif" font-size="48" font-weight="700" letter-spacing="0.4">havline</text>
<text x="142" y="92" fill="currentColor" font-family="SFMono-Regular, Consolas, monospace" font-size="12" letter-spacing="1.5" opacity="0.62">SAFE LINE FOR EVERY SERVICE</text>
<line x1="142" y1="108" x2="522" y2="108" stroke="currentColor" stroke-width="1" opacity="0.16"></line>
</svg>
</article>
<article class="lockup-card lockup-card--light">
<div class="lockup-compact">
<svg viewBox="0 0 96 96" role="img" aria-label="Havline 紧凑标志">
<use href="#havline-mark"></use>
</svg>
<span class="lockup-compact__word">havline</span>
</div>
</article>
</div>
</div>
</section>
<section class="section">
<div class="wrap">
<div class="section__head">
<h2>SVG 源文件</h2>
<p>右侧按钮会下载一个独立的 SVG 文件,可以直接用于后续应用图标、favicon、飞牛应用包和前端静态资源。</p>
</div>
<div class="code-shell">
<pre><code id="svg-code"></code></pre>
<div class="inline-row">
<button class="control control--primary" type="button" id="copy-mark">复制 SVG</button>
<button class="control" type="button" id="download-mark-2">下载 SVG</button>
</div>
</div>
</div>
</section>
</main>
<footer class="wrap footnote">
Havline logo study · 基于“港湾、连续线路、服务节点”三个关键词生成 · 所有图形均由内联 SVG 构成,可无损缩放。
</footer>
<script>
const root = document.documentElement;
const themeButtons = document.querySelectorAll("[data-theme-set]");
function setTheme(theme) {
root.dataset.theme = theme;
themeButtons.forEach((button) => {
button.setAttribute("aria-pressed", String(button.dataset.themeSet === theme));
});
}
themeButtons.forEach((button) => {
button.addEventListener("click", () => setTheme(button.dataset.themeSet));
});
const sizeInput = document.getElementById("mark-size");
const sizeFigure = document.getElementById("size-figure");
const sizeReadout = document.getElementById("size-readout");
function updateSize() {
const size = `${sizeInput.value}px`;
sizeFigure.style.setProperty("--preview-size", size);
sizeReadout.value = `${sizeInput.value} px`;
sizeReadout.textContent = `${sizeInput.value} px`;
}
sizeInput.addEventListener("input", updateSize);
updateSize();
const exportMark = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" role="img" aria-label="Havline logo">
<defs>
<linearGradient id="havline-bg" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#0e4b68"/>
<stop offset="1" stop-color="#061827"/>
</linearGradient>
<linearGradient id="havline-line" x1="0" y1="1" x2="1" y2="0">
<stop offset="0" stop-color="#2fa9ff"/>
<stop offset="1" stop-color="#63f0d0"/>
</linearGradient>
</defs>
<rect x="4" y="4" width="88" height="88" rx="22" fill="url(#havline-bg)"/>
<rect x="4.8" y="4.8" width="86.4" height="86.4" rx="21.2" fill="none" stroke="rgba(242,255,252,.13)" stroke-width="1.6"/>
<path d="M31 25.5V70.5" stroke="#f2fffc" stroke-width="11" stroke-linecap="round"/>
<path d="M65 25.5V70.5" stroke="#f2fffc" stroke-width="11" stroke-linecap="round"/>
<path d="M31 59C39 59 42.5 49 49.5 49C56.5 49 57.5 37 65 37" stroke="url(#havline-line)" stroke-width="7" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="31" cy="59" r="5" fill="#2fa9ff" stroke="#061827" stroke-width="2.2"/>
<circle cx="65" cy="37" r="5" fill="#63f0d0" stroke="#061827" stroke-width="2.2"/>
</svg>`;
const svgCode = document.getElementById("svg-code");
svgCode.textContent = exportMark;
function downloadMark() {
const blob = new Blob([exportMark], { type: "image/svg+xml;charset=utf-8" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "havline-logo.svg";
document.body.appendChild(link);
link.click();
link.remove();
URL.revokeObjectURL(url);
}
document.getElementById("download-mark").addEventListener("click", downloadMark);
document.getElementById("download-mark-2").addEventListener("click", downloadMark);
document.getElementById("copy-mark").addEventListener("click", async (event) => {
try {
await navigator.clipboard.writeText(exportMark);
event.currentTarget.textContent = "已复制";
setTimeout(() => {
event.currentTarget.textContent = "复制 SVG";
}, 1400);
} catch {
event.currentTarget.textContent = "复制失败";
setTimeout(() => {
event.currentTarget.textContent = "复制 SVG";
}, 1400);
}
});
</script>
</body>
</html>