The palette lived as ~620 hex literals in inline styles plus a few hundred more in
the stylesheets, so there was no single thing to change. It is now 113 custom
properties in src/styles/theme.css, in two blocks.
src/scripts/theme-boot.js decides the theme before first paint — blocking, in <head>,
above the stylesheet, because a deferred script paints dark first and corrects itself.
It stores the mode ('system' | 'light' | 'dark'), never the colour it resolved to, and
stamps data-theme so an explicit choice can beat the media query. The switcher in the
header is a view onto it.
A filled accent stays the brand colour in both themes — the ink on it is near-black
either way — while an accent used as text darkens to clear 4.5:1 on white. A colour
reaches a fill by four routes (a style property, a constant, a helper argument, an SVG
source string), and tests/theme-switching.test.mjs covers all four.
The dark theme is unchanged: every colour declaration the previous build produced comes
out of this one identically once the properties are resolved.
Also: the roadmap drops its status chips on mobile, and Roadmap.jsx no longer splits a
colour with parseInt at runtime, which a var() reference cannot survive.
122 lines
2.6 KiB
JSON
122 lines
2.6 KiB
JSON
{
|
|
"name": "SecureBit.chat - 私密的端到端加密通讯工具",
|
|
"short_name": "SecureBit",
|
|
"description": "采用 ECDH、DTLS 与 SAS 防护、军用级密码学的 P2P 通讯工具",
|
|
"start_url": "/zh/",
|
|
"display": "standalone",
|
|
"background_color": "#0f0f11",
|
|
"theme_color": "#0f0f11",
|
|
"orientation": "portrait-primary",
|
|
"scope": "/",
|
|
"lang": "zh-Hans",
|
|
"dir": "ltr",
|
|
"categories": [
|
|
"communication",
|
|
"security",
|
|
"productivity"
|
|
],
|
|
"prefer_related_applications": false,
|
|
"icons": [
|
|
{
|
|
"src": "/logo/icon-72x72.png",
|
|
"sizes": "72x72",
|
|
"type": "image/png",
|
|
"purpose": "any"
|
|
},
|
|
{
|
|
"src": "/logo/icon-96x96.png",
|
|
"sizes": "96x96",
|
|
"type": "image/png",
|
|
"purpose": "any"
|
|
},
|
|
{
|
|
"src": "/logo/icon-128x128.png",
|
|
"sizes": "128x128",
|
|
"type": "image/png",
|
|
"purpose": "any"
|
|
},
|
|
{
|
|
"src": "/logo/icon-144x144.png",
|
|
"sizes": "144x144",
|
|
"type": "image/png",
|
|
"purpose": "any"
|
|
},
|
|
{
|
|
"src": "/logo/icon-152x152.png",
|
|
"sizes": "152x152",
|
|
"type": "image/png",
|
|
"purpose": "any"
|
|
},
|
|
{
|
|
"src": "/logo/icon-180x180.png",
|
|
"sizes": "180x180",
|
|
"type": "image/png",
|
|
"purpose": "any"
|
|
},
|
|
{
|
|
"src": "/logo/icon-192x192.png",
|
|
"sizes": "192x192",
|
|
"type": "image/png",
|
|
"purpose": "any"
|
|
},
|
|
{
|
|
"src": "/logo/icon-256x256.png",
|
|
"sizes": "256x256",
|
|
"type": "image/png",
|
|
"purpose": "any"
|
|
},
|
|
{
|
|
"src": "/logo/icon-384x384.png",
|
|
"sizes": "384x384",
|
|
"type": "image/png",
|
|
"purpose": "any"
|
|
},
|
|
{
|
|
"src": "/logo/icon-512x512.png",
|
|
"sizes": "512x512",
|
|
"type": "image/png",
|
|
"purpose": "any"
|
|
},
|
|
{
|
|
"src": "/logo/icon-192x192.png",
|
|
"sizes": "192x192",
|
|
"type": "image/png",
|
|
"purpose": "maskable"
|
|
},
|
|
{
|
|
"src": "/logo/icon-512x512.png",
|
|
"sizes": "512x512",
|
|
"type": "image/png",
|
|
"purpose": "maskable"
|
|
}
|
|
],
|
|
"shortcuts": [
|
|
{
|
|
"name": "Create Channel",
|
|
"short_name": "Create",
|
|
"description": "Create a new secure channel",
|
|
"url": "/zh/?action=create",
|
|
"icons": [
|
|
{
|
|
"src": "/logo/icon-96x96.png",
|
|
"sizes": "96x96",
|
|
"type": "image/png"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"name": "Join Channel",
|
|
"short_name": "Join",
|
|
"description": "Join an existing secure channel",
|
|
"url": "/zh/?action=join",
|
|
"icons": [
|
|
{
|
|
"src": "/logo/icon-96x96.png",
|
|
"sizes": "96x96",
|
|
"type": "image/png"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|