新站标「一横」:墨圆 + 一道白横,接进两套 UI 与图标

立意:既是字母 O 也是汉字「一」,对应站名 ONE / 一个博客。

- favicon.svg 重画:横杠用 fill-rule=evenodd 挖成真洞而不是叠一块纸色矩形,
  所以纸感 / 墨色暗端 / 后台卡片任何底色都直接透过去,不必为皮肤各出一版;
  SVG 内带 prefers-color-scheme,标签页深浅自动翻色
- 补 favicon-16/32.png(不认 SVG 的老浏览器)与 apple-touch-icon.png 180
  (iOS 不支持 SVG 且会把透明压成黑底,故铺纸色实底)。PNG 由临时 Go 程序
  按 8x8 子采样光栅化生成,生成器未入库,产物是资产
- LogoMark.vue 内联 SVG 组件:颜色跟 currentColor,尺寸由外层给
- 接入 6 处:classic 桌面左栏品牌位(原来是个排版字符 ○)、移动顶栏 brand、
  时间线头像位、详情页头像位、快发盒头像位;vivid 顶栏与底部 dock 品牌标记
  (右下角绿色在线点保留,它是「有人在写」的信号,不属于被替换的部分)
- 顺带清理:三处只为算首字母的死代码(initial 与只服务于它的 userName)、
  vivid 里一条被同特指度靠后规则完全覆盖的 .av 死规则;标记 wrapper 统一加
  flex:none,否则站名一长会被压成椭圆
This commit is contained in:
Sakurasan committed 2026-09-29 21:04:55 +08:00
1 parent e6e4bdc71c
commit 7e302c51a6
13 files changed
+87 -84

No files matched your search

+6
View File
@@ -7,7 +7,13 @@
<meta name="theme-color" content="#1f1d1a" media="(prefers-color-scheme: dark)" />
<title>ONE · 一个博客</title>
<meta name="description" content="长文与短文,同一种节奏。" />
<!-- 方案 A「一横」:SVG 优先(内部有 prefers-color-scheme,深浅皮肤各自变色),
PNG 只给不认 SVG 的老浏览器;apple-touch-icon 必须 PNG(iOS 不支持 SVG,
且会把透明压成黑底,所以那张铺了纸色实底)。 -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16.png" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml" />
<!--
UI 版本在 <html> 上以 data-ui 表达,服务端设置要等 /api/site 才知道。
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

+10 -32
View File
@@ -1,32 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 128.81 128.17">
<defs>
<style>
.cls-1 {
fill: url(#gradient3);
}
.cls-2 {
fill: #000;
}
@media (prefers-color-scheme: dark) {
.cls-2 {
fill: #fff;
}
}
</style>
<linearGradient id="gradient3" x1="57.54" y1="31.44" x2="128.81" y2="31.44" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#ff4d4d"/>
<stop offset=".99" stop-color="#f9cb28"/>
</linearGradient>
</defs>
<g id="b">
<g>
<path class="cls-2" d="M56.32,100.47c0,15.98-11.84,27.61-27.85,27.61S.33,116.45,.33,100.47s11.83-27.45,28.08-27.45,27.91,11.63,27.91,27.45Z"/>
<path class="cls-2" d="M120,101.26v19.31c-4.87,4.63-12.9,7.6-21.65,7.6-18.33,0-30-10.49-30-26.7s12.92-28.34,31.07-28.34v28.13h20.58Z"/>
<polygon class="cls-1" points="119.7 21.48 128.81 38.51 83.27 62.88 57.54 14.79 85.17 0 101.79 31.06 119.7 21.48"/>
<polygon class="cls-2" points="0 63.11 16.54 32.82 .68 8.17 55.98 8.17 40.12 32.82 56.65 63.11 0 63.11"/>
</g>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="ONE">
<!-- 方案 A「一横」:墨圆 + 一道白横,既是字母 O 也是汉字「一」。
横杠用 fill-rule="evenodd" 挖成真洞(不是叠一块纸色矩形),
所以它在任何底色上都是透明的——不需要为深浅皮肤各出一版。 -->
<style>
.m { fill: #33302b }
@media (prefers-color-scheme: dark) { .m { fill: #e8e3d9 } }
</style>
<path class="m" fill-rule="evenodd" d="M32 4a28 28 0 1 0 0 56 28 28 0 1 0 0-56Zm-16 23.5h32a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1H16a1 1 0 0 1-1-1v-7a1 1 0 0 1 1-1Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 648 B

+8 -3
View File
@@ -3,6 +3,7 @@ import { computed } from 'vue'
import { site } from '../site'
import { socialIcon } from '../socialIcons'
import ThemeSwitcher from './ThemeSwitcher.vue'
import LogoMark from './LogoMark.vue'
// vivid 用顶部导航(VividNav)代替左栏。这里直接不渲染。
const isVivid = computed(() => site.ui_id === 'vivid')
@@ -15,7 +16,7 @@ const social = computed(() => (site.social_links || []).filter((s) => s && s.url
<aside v-if="!isVivid" class="rail-left">
<div class="inner">
<RouterLink to="/" class="brand">
<span class="mark">○</span>
<span class="mark"><LogoMark /></span>
<span class="name">{{ site.site_title || 'ONE' }}</span>
</RouterLink>
<p class="desc">{{ site.site_desc }}</p>
@@ -86,9 +87,13 @@ const social = computed(() => (site.social_links || []).filter((s) => s && s.url
gap: 8px;
}
/* 原来这里是一个排版字符 ○ 充当标记,换成真站标。
flex:none:它是 .brand(flex)的子项,不锁住会被长站名压成椭圆 */
.mark {
color: var(--accent);
font-size: 18px;
width: 22px;
height: 22px;
flex: none;
color: var(--ink);
}
.name {
+26
View File
@@ -0,0 +1,26 @@
<script setup>
// 站标「一横」:墨圆 + 一道白横 —— 字母 O 与汉字「一」同形,对应站名 ONE / 一个博客。
//
// 横杠是 fill-rule="evenodd" 挖出来的真洞,不是叠一块纸色矩形,所以任何底色
// (纸感 / 墨色暗端 / 后台卡片)都直接透过去,不必为皮肤各出一版。
// 颜色跟随 currentColor,尺寸由外层容器决定。
</script>
<template>
<svg class="logo-mark" viewBox="0 0 64 64" aria-hidden="true" focusable="false">
<path
fill="currentColor"
fill-rule="evenodd"
d="M32 4a28 28 0 1 0 0 56 28 28 0 1 0 0-56Zm-16 23.5h32a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1H16a1 1 0 0 1-1-1v-7a1 1 0 0 1 1-1Z"
/>
</svg>
</template>
<style scoped>
.logo-mark {
display: block;
width: 100%;
height: 100%;
flex-shrink: 0;
}
</style>
+5 -12
View File
@@ -5,6 +5,7 @@ import { site } from '../site'
import { openLightbox } from '../lightbox'
import { relativeDate, stripTags, minutesLabel, sanitizeHtml, thumbURL, thumbifyHtml } from '../utils'
import LinkCard from './LinkCard.vue'
import LogoMark from './LogoMark.vue'
const router = useRouter()
@@ -34,15 +35,13 @@ const summaryText = computed(() => {
return text.length > 110 ? text.slice(0, 110) + '…' : text
})
const initial = computed(() => (site.author_name || 'O').trim().slice(0, 1).toUpperCase())
// 配图缩略图宽度:单图全宽格取宽一些,多图小格 480 足够(2x 屏也清晰)
const gridW = computed(() => (props.post.images && props.post.images.length === 1 ? 720 : 480))
</script>
<template>
<article class="row-feed" :class="{ short: isShort }" @click="openPost">
<div class="avatar" aria-hidden="true">{{ initial }}</div>
<div class="avatar"><LogoMark /></div>
<div class="body">
<div class="byline">
@@ -126,18 +125,12 @@ const gridW = computed(() => (props.post.images && props.post.images.length ===
background: var(--paper-sunken);
}
/* 站标本身就是个圆,所以原来那层 accent-soft 圆底 / 衬线首字母都不需要了,
只留尺寸与颜色。想换回降饱和蓝只需改 color 一行。 */
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--accent-soft);
color: var(--accent);
display: flex;
align-items: center;
justify-content: center;
font-family: var(--serif);
font-size: 17px;
user-select: none;
color: var(--ink);
}
.byline {
+4 -11
View File
@@ -1,6 +1,7 @@
<script setup>
import { computed, onMounted, ref } from 'vue'
import { adminApi } from '../api'
import LogoMark from './LogoMark.vue'
// Twitter 式短文发布盒,挂在时间线顶部。
//
@@ -17,7 +18,6 @@ import { adminApi } from '../api'
const emit = defineEmits(['published'])
const shown = ref(false)
const userName = ref('')
const draft = ref('')
const publishing = ref(false)
const error = ref('')
@@ -53,7 +53,6 @@ function removeImage(i) {
images.value.splice(i, 1)
}
const initial = computed(() => (userName.value || 'O').trim().slice(0, 1).toUpperCase())
const canPost = computed(
() => (draft.value.trim().length > 0 || images.value.length > 0) && !publishing.value
)
@@ -62,7 +61,6 @@ onMounted(async () => {
try {
const d = await adminApi.me()
if (d.user) {
userName.value = String(d.user)
shown.value = true
}
} catch {
@@ -110,7 +108,7 @@ async function publish() {
<!-- 桌面:内联 -->
<div class="qs-inline">
<div class="qs-card">
<span class="qs-av">{{ initial }}</span>
<span class="qs-av"><LogoMark /></span>
<div class="qs-body">
<textarea v-model="draft" class="qs-input" rows="2" placeholder="记点短的…" @keydown="onKey" />
<div v-if="images.length" class="qs-thumbs">
@@ -197,17 +195,12 @@ async function publish() {
background: var(--card);
}
/* 站标自身是圆,去掉原来的 accent-soft 圆底与首字母字号;flex:none 防被压扁 */
.qs-av {
flex: none;
width: 34px;
height: 34px;
border-radius: 50%;
background: var(--accent-soft);
color: var(--accent);
display: grid;
place-items: center;
font-weight: 700;
font-size: 14px;
color: var(--ink);
}
.qs-body {
+15 -1
View File
@@ -3,6 +3,7 @@ import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { RouterLink, useRoute } from 'vue-router'
import { site } from '../site'
import NavBurger from './NavBurger.vue'
import LogoMark from './LogoMark.vue'
// 手机端导航。原来把 5 个链接平铺在顶栏里:375px 挤成一排小字,
// 触点只有十几像素高,320px 的屏还会折行。改成 brand + 汉堡钮,
@@ -105,7 +106,10 @@ onBeforeUnmount(() => {
<template>
<!-- 窄屏时左栏收起,导航回到顶部;下滑隐藏、上滑唤回 -->
<header class="topbar" :class="{ hidden }">
<RouterLink to="/" class="brand">{{ site.site_title || 'ONE' }}</RouterLink>
<RouterLink to="/" class="brand">
<span class="mk"><LogoMark /></span>
<span>{{ site.site_title || 'ONE' }}</span>
</RouterLink>
<NavBurger
ref="burger"
class="burger"
@@ -179,6 +183,16 @@ onBeforeUnmount(() => {
font-family: var(--serif);
font-size: 17px;
color: var(--ink);
display: flex;
align-items: center;
gap: 8px;
}
.brand .mk {
width: 20px;
height: 20px;
flex: none;
color: var(--ink);
}
.burger {
+3 -3
View File
@@ -3,6 +3,7 @@ import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { RouterLink, useRoute } from 'vue-router'
import { site } from '../../site'
import NavBurger from '../../components/NavBurger.vue'
import LogoMark from '../../components/LogoMark.vue'
// 余白顶栏。对应设计稿的 <header class="top">:品牌 / 导航 / 操作三块,
// 滚动过 8px 加 .scrolled(设计稿的「四态里的第一态」)。
@@ -27,7 +28,6 @@ const links = [
{ to: '/about', label: '关于', en: 'About' }
]
const initial = () => (site.author_name || site.site_title || 'O').trim().slice(0, 1).toUpperCase()
const title = () => site.site_title?.split(/[·|]/)[0]?.trim() || 'ONE'
function isOn(l) {
@@ -94,7 +94,7 @@ function toggleTheme() {
<header class="top" :class="{ scrolled }">
<div class="top-in">
<RouterLink class="brand" to="/">
<span class="av">{{ initial() }}</span>
<span class="av"><LogoMark /></span>
<span>{{ title() }}</span>
</RouterLink>
<nav class="nav" aria-label="站点导航">
@@ -115,7 +115,7 @@ function toggleTheme() {
必须待在 .top 外面:.top 有 backdrop-filter,会给 fixed 后代当包含块。 -->
<div class="dock">
<RouterLink class="dock-brand" to="/">
<span class="av">{{ initial() }}</span>
<span class="av"><LogoMark /></span>
<span>{{ title() }}</span>
</RouterLink>
<NavBurger
+6 -12
View File
@@ -15,15 +15,8 @@ html[data-ui='vivid'] .brand {display:flex;align-items:center;gap:10px;
@media (hover: hover) and (pointer: fine) {
html[data-ui='vivid'] .brand:hover {transform:scale(1.04)}
}
/* .av 品牌标记:顶栏一枚,菜单里那枚随菜单头一起撤了(顶栏浮在菜单上) */
html[data-ui='vivid'] .brand .av {width:30px;height:30px;border-radius:50%;
background:linear-gradient(135deg,var(--accent),var(--accent-deep));
display:grid;place-items:center;color:#fff;font-size:13px;font-weight:800;
box-shadow:0 0 0 2px var(--paper),0 0 0 3.5px var(--accent-soft);
position:relative;}
html[data-ui='vivid'] .brand .av::after {content:"";position:absolute;right:-1px;bottom:-1px;
width:9px;height:9px;border-radius:50%;
background:#34c759;border:2px solid var(--paper);}
/* .av 品牌标记:原来这里是一条 .brand .av 规则,但它被下面 :is(.brand,.dock-brand)
那条同特指度、且位置更靠后的规则完整覆盖了——等于死规则。合并成一条,避免两处漂移。 */
html[data-ui='vivid'] .nav {display:flex;gap:2px;flex:1;justify-content:center}
html[data-ui='vivid'] .nav a {position:relative;font-size:13.5px;font-weight:600;color:var(--soft);
padding:8px 14px;border-radius:999px;
@@ -74,9 +67,10 @@ html[data-ui='vivid'] .dock-brand {display:flex;align-items:center;gap:9px;
@media (max-width:720px) {
html[data-ui='vivid'] .dock {display:flex}
}
html[data-ui='vivid'] :is(.brand, .dock-brand) .av {width:30px;height:30px;border-radius:50%;
background:linear-gradient(135deg,var(--accent),var(--accent-deep));
display:grid;place-items:center;color:#fff;font-size:13px;font-weight:800;
/* 品牌标记换成站标「一横」:它自身就是圆,所以去掉渐变圆盘与首字母字号;
右下角那颗绿色在线点保留(它是「有人在写」的信号,不属于被替换的部分)。 */
html[data-ui='vivid'] :is(.brand, .dock-brand) .av {width:30px;height:30px;flex:none;
color:var(--ink);
box-shadow:0 0 0 2px var(--paper),0 0 0 3.5px var(--accent-soft);
position:relative;}
html[data-ui='vivid'] :is(.brand, .dock-brand) .av::after {content:"";position:absolute;right:-1px;bottom:-1px;
+4 -10
View File
@@ -6,6 +6,7 @@ import RightRail from '../components/RightRail.vue'
import YohakuArticle from '../ui/yohaku/YohakuArticle.vue'
import CommentSection from '../components/comments/CommentSection.vue'
import LinkCard from '../components/LinkCard.vue'
import LogoMark from '../components/LogoMark.vue'
import { openLightbox } from '../lightbox'
import { publicApi } from '../api'
import { site, applyDocTitle } from '../site'
@@ -68,7 +69,6 @@ onBeforeUnmount(() => window.removeEventListener('scroll', onScroll))
watch(() => route.params.slug, load)
const isShort = computed(() => post.value && post.value.kind === 'short')
const initial = computed(() => (site.author_name || 'O').trim().slice(0, 1).toUpperCase())
</script>
<template>
@@ -100,7 +100,7 @@ const initial = computed(() => (site.author_name || 'O').trim().slice(0, 1).toUp
<article v-else class="wrap" :class="{ short: isShort }">
<header class="head">
<div class="byline">
<div class="avatar">{{ initial }}</div>
<div class="avatar"><LogoMark /></div>
<div class="who">
<span class="author">{{ site.author_name || 'ONE' }}</span>
<span class="sub">
@@ -188,17 +188,11 @@ const initial = computed(() => (site.author_name || 'O').trim().slice(0, 1).toUp
gap: 12px;
}
/* 站标自身是圆,原来的 accent-soft 圆底与衬线首字母一并去掉,只留尺寸与颜色 */
.avatar {
width: 44px;
height: 44px;
border-radius: 50%;
background: var(--accent-soft);
color: var(--accent);
display: flex;
align-items: center;
justify-content: center;
font-family: var(--serif);
font-size: 18px;
color: var(--ink);
}
.who {