Skip to content

Commit 0e5c6c4

Browse files
authored
Fix CJK fonts again and misc. font issues (#14575)
* Push system-ui further down the stack, fix #12966 * Fix Firefox showing U+300x in emoji font and more * Revert emoji font and fix long-standing Safari bug * Exclude Safari emoji fix above 1.25x zoom * Minor correctness/typo fix, affects only legacy platforms * Emoji consistency for monospace (e.g. EasyMDE) * Override paradigm; macOS/iOS-specific metric fix * Move whitespace fix to font-face * Handle metric calculation errors with Firefox * One last workaround for aliased fonts in Linux
1 parent c0c052b commit 0e5c6c4

File tree

6 files changed

+187
-40
lines changed

6 files changed

+187
-40
lines changed

templates/base/head.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!DOCTYPE html>
2-
<html lang="{{.Language}}" class="theme-{{.SignedUser.Theme}}">
2+
<html lang="{{.Lang}}" class="theme-{{.SignedUser.Theme}}">
33
<head data-suburl="{{AppSubUrl}}">
44
<meta charset="utf-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1">

web_src/js/index.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {initNotificationsTable, initNotificationCount} from './features/notifica
2626
import {initStopwatch} from './features/stopwatch.js';
2727
import {createCodeEditor, createMonaco} from './features/codeeditor.js';
2828
import {svg, svgs} from './svg.js';
29-
import {stripTags} from './utils.js';
29+
import {stripTags, mqBinarySearch} from './utils.js';
3030

3131
const {AppSubUrl, StaticUrlPrefix, csrf} = window.config;
3232

@@ -2519,6 +2519,19 @@ $(document).ready(async () => {
25192519
.attr('title', '');
25202520
});
25212521

2522+
// Undo Safari emoji glitch fix at high enough zoom levels
2523+
if (navigator.userAgent.match('Safari')) {
2524+
$(window).resize(() => {
2525+
const px = mqBinarySearch('width', 0, 4096, 1, 'px');
2526+
const em = mqBinarySearch('width', 0, 1024, 0.01, 'em');
2527+
if (em * 16 * 1.25 - px <= -1) {
2528+
$('body').addClass('safari-above125');
2529+
} else {
2530+
$('body').removeClass('safari-above125');
2531+
}
2532+
});
2533+
}
2534+
25222535
// Semantic UI modules.
25232536
$('.dropdown:not(.custom)').dropdown({
25242537
fullTextSearch: 'exact'

web_src/js/utils.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,16 @@ export function uniq(arr) {
2828
export function stripTags(text) {
2929
return text.replace(/<[^>]*>?/gm, '');
3030
}
31+
32+
// searches the inclusive range [minValue, maxValue].
33+
// credits: https://matthiasott.com/notes/write-your-media-queries-in-pixels-not-ems
34+
export function mqBinarySearch(feature, minValue, maxValue, step, unit) {
35+
if (maxValue - minValue < step) {
36+
return minValue;
37+
}
38+
const mid = Math.ceil((minValue + maxValue) / 2 / step) * step;
39+
if (matchMedia(`screen and (min-${feature}:${mid}${unit})`).matches) {
40+
return mqBinarySearch(feature, mid, maxValue, step, unit); // feature is >= mid
41+
}
42+
return mqBinarySearch(feature, minValue, mid - step, step, unit); // feature is < mid
43+
}

web_src/less/_base.less

Lines changed: 28 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
:root {
22
/* documented customizable variables */
3-
--fonts-proportional: system-ui, -apple-system, "BlinkMacSystemFont", "Segoe UI", "Roboto", "Helvetica Neue", "Arial", "Noto Sans", "Liberation Sans", sans-serif;
4-
--fonts-monospace: "SFMono-Regular", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", monospace;
5-
--fonts-emoji: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", "Twemoji Mozilla";
3+
--fonts-proportional: -apple-system, "Segoe UI", system-ui, "Roboto", "Helvetica Neue", "Arial";
4+
--fonts-monospace: "SFMono-Regular", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", monospace, var(--fonts-emoji);
5+
--fonts-emoji: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla";
66
/* other variables */
7-
--fonts-regular: var(--fonts-proportional), var(--fonts-emoji);
87
--border-radius: .28571429rem;
98
--opacity-disabled: .55;
109
--color-primary: #4183c4;
@@ -115,36 +114,8 @@
115114
--checkbox-mask-indeterminate: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2 7.75A.75.75 0 012.75 7h10a.75.75 0 010 1.5h-10A.75.75 0 012 7.75z"></path></svg>');
116115
}
117116

118-
:root:lang(ja) {
119-
--fonts-proportional: "Hiragino Kaku Gothic ProN", "Yu Gothic", "Source Han Sans JP", "Noto Sans CJK JP", "Droid Sans Japanese", "Meiryo", "MS PGothic";
120-
}
121-
122-
:root:lang(zh-CN) {
123-
--fonts-proportional: "PingFang SC", "Hiragino Sans GB", "Source Han Sans CN", "Source Han Sans SC", "Noto Sans CJK SC", "Microsoft YaHei", "Heiti SC", "SimHei";
124-
}
125-
126-
:root:lang(zh-TW) {
127-
--fonts-proportional: "PingFang TC", "Hiragino Sans TC", "Source Han Sans TW", "Source Han Sans TC", "Noto Sans CJK TC", "Microsoft JhengHei", "Heiti TC", "PMingLiU";
128-
}
129-
130-
:root:lang(zh-HK) {
131-
--fonts-proportional: "PingFang HK", "Hiragino Sans TC", "Source Han Sans HK", "Source Han Sans TC", "Noto Sans CJK TC", "Microsoft JhengHei", "Heiti TC", "PMingLiU_HKSCS", "PMingLiU";
132-
}
133-
134-
:root:lang(ko) {
135-
--fonts-proportional: "Apple SD Gothic Neo", "NanumBarunGothic", "Malgun Gothic", "Gulim", "Dotum", "Nanum Gothic", "Source Han Sans KR", "Noto Sans CJK KR";
136-
}
137-
138-
@font-face {
139-
font-family: "Yu Gothic";
140-
src: local("Yu Gothic Medium");
141-
font-weight: 400;
142-
}
143-
144-
@font-face {
145-
font-family: "Yu Gothic";
146-
src: local("Yu Gothic Bold");
147-
font-weight: 500;
117+
:root * {
118+
--fonts-regular: var(--fonts-override, var(--fonts-proportional)), "Noto Sans", "Liberation Sans", var(--fonts-emoji), sans-serif;
148119
}
149120

150121
textarea {
@@ -1151,10 +1122,18 @@ footer {
11511122
}
11521123
}
11531124

1154-
.ui.language .menu {
1155-
max-height: 500px;
1156-
overflow-y: auto;
1157-
margin-bottom: 7px;
1125+
.ui.language {
1126+
.menu {
1127+
max-height: 500px;
1128+
overflow-y: auto;
1129+
margin-bottom: 7px;
1130+
}
1131+
1132+
.svg {
1133+
margin-right: .15em;
1134+
vertical-align: top;
1135+
margin-top: calc(2em - 16px);
1136+
}
11581137
}
11591138

11601139
.ui {
@@ -1885,6 +1864,17 @@ table th[data-sortt-desc] {
18851864
font-style: normal !important;
18861865
font-weight: normal !important;
18871866
vertical-align: -.075em;
1867+
1868+
@supports (-webkit-hyphens:none) {
1869+
body:not(.safari-above125) & {
1870+
font-size: inherit;
1871+
vertical-align: inherit;
1872+
img {
1873+
font-size: 1.25em;
1874+
vertical-align: -.225em !important;
1875+
}
1876+
}
1877+
}
18881878
}
18891879

18901880
.emoji img,

web_src/less/_font_i18n.less

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/* font i18n */
2+
:root {
3+
/* customizable localized variables */
4+
:lang(ja) {
5+
--fonts-override: var(--fonts-default-override-ja);
6+
}
7+
:lang(zh-CN) {
8+
--fonts-override: var(--fonts-default-override-zh-cn);
9+
}
10+
:lang(zh-TW) {
11+
--fonts-override: var(--fonts-default-override-zh-tw);
12+
}
13+
:lang(zh-HK) {
14+
--fonts-override: var(--fonts-default-override-zh-hk);
15+
}
16+
:lang(ko) {
17+
--fonts-override: var(--fonts-default-override-ko);
18+
}
19+
}
20+
21+
[lang] {
22+
font-family: var(--fonts-regular);
23+
}
24+
25+
each(@fonts, {
26+
@weights: .gen-weights-all(@value);
27+
@locale: replace(@key, "@", "-");
28+
.font-face-cjk(~"system-ui@{locale}", @weights[@light], 300);
29+
.font-face-cjk(~"system-ui@{locale}", @weights[@regular], 400);
30+
.font-face-cjk(~"system-ui@{locale}", @weights[@medium], 500);
31+
.font-face-cjk(~"system-ui@{locale}", @weights[@bold], 700);
32+
/* Safari on macOS/iOS */
33+
@font-face {
34+
font-family: ~"system-ui@{locale}";
35+
src: local("HelveticaNeue");
36+
unicode-range: U+A0;
37+
}
38+
/* Other browsers on macOS/iOS */
39+
@supports not (-webkit-hyphens:none) {
40+
@font-face {
41+
font-family: ~"system-ui@{locale}";
42+
src: local("HelveticaNeue");
43+
unicode-range: U+20;
44+
}
45+
}
46+
:root {
47+
/* Special handling for Firefox on Windows/Linux */
48+
@supports (-moz-appearance:none) {
49+
--fonts-default-override@{locale}: ~"var(--fonts-proportional), system-ui@{locale}";
50+
}
51+
--fonts-default-override@{locale}: ~"system-ui@{locale}, var(--fonts-proportional)";
52+
}
53+
});
54+
55+
@fonts: {
56+
@ja:
57+
"HiraginoSans-:{W2,W4,W5,W6}", "HiraKakuProN-:{W3,W6}", "Hiragino Kaku Gothic ProN :{W3,W6}",
58+
.shs("JP")[], .shs("J")[], .noto("JP")[], .shs("")[],
59+
/* https://acetaminophen.hatenablog.com/entry/2016/02/15/225009 */
60+
"Yu Gothic :{Regular,Medium,Bold}", "YuGothic :{Regular,Medium,Bold}",
61+
"Droid Sans Japanese:{}", "Meiryo:{, Bold}", "MS PGothic:{}";
62+
@zh-cn:
63+
.pingfang("SC")[],
64+
.shs("CN")[], .shs("SC")[], .noto("SC")[],
65+
"HiraginoSansGB-:{W3,W6}", "Hiragino Sans GB :{W3,W6}",
66+
"Microsoft YaHei:{ Light,, Bold}", "Heiti SC :{Light,Medium}", "SimHei:{}";
67+
@zh-tw:
68+
.pingfang("TC")[],
69+
.shs("TW")[], .shs("TC")[], .noto("TC")[],
70+
"HiraginoSansTC-:{W3,W6}", "Hiragino Sans TC :{W3,W6}",
71+
"Microsoft JhengHei:{ Light,, Bold}", "Heiti TC :{Light,Medium}", "PMingLiU:{}";
72+
@zh-hk:
73+
.pingfang("HK")[],
74+
.shs("HK")[], .shs("HC")[], .noto("HK")[], .shs("TC")[], .noto("TC")[],
75+
"HiraginoSansTC-:{W3,W6}", "Hiragino Sans TC :{W3,W6}",
76+
"Microsoft JhengHei:{ Light,, Bold}", "Heiti TC :{Light,Medium}", "PMingLiU_HKSCS:{}", "PMingLiU:{}";
77+
@ko:
78+
"AppleSDGothicNeo-:{Light,Regular,Medium,SemiBold}",
79+
.shs("KR")[], .shs("K")[], .noto("KR")[],
80+
"NanumBarunGothic:{ Light,, Bold}",
81+
"Malgun Gothic:{ Semilight,, Bold}", "Nanum Gothic:{, Bold}", "Dotum:{}";
82+
}
83+
84+
.noto(@suffix) { @value: "Noto Sans CJK @{suffix} ", "NotoSansCJK@{suffix}-"; }
85+
.shs(@suffix) { @value: replace("Source Han Sans @{suffix} ", " ", " "), "SourceHanSans@{suffix}-"; }
86+
.pingfang(@suffix) { @value: "PingFang@{suffix}-:{Light,Regular,Medium,Semibold}"; }
87+
.font-face-cjk(@family, @src, @weight) {
88+
@font-face {
89+
font-family: @family;
90+
src: @src;
91+
font-weight: @weight;
92+
unicode-range: ~"U+11??, U+2E80-4DBF, U+4E00-9FFF, U+A960-A97F, U+AC00-D7FF, U+F900-FAFF, U+FE00-FE6F, U+FF00-FFEF, U+1F2??, U+2????";
93+
}
94+
}
95+
96+
.gen-weights(@family) when (isstring(@family)) {
97+
@family-str: replace(@family, ":\{.*\}$", "");
98+
// apply standard style names if none is given
99+
// should the font have no styles, use :{}, as in "SimHei:{}"
100+
@weights-str: if(@family = @family-str, "Light,Regular,Medium,Bold", replace(@family, ".*:\{(.*)\}$", "$1"));
101+
@lightest: replace(@weights-str, ",.*", "");
102+
@boldest: replace(@weights-str, ".*,", "");
103+
@2ndboldest: replace(@weights-str, "(?:.*,|)([^,]*),.*$", "$1");
104+
@2ndlightest: if(@2ndboldest = @lightest, @lightest, replace(@weights-str, "^.*?,([^,]*).*", "$1"));
105+
106+
@light: local("@{family-str}@{lightest}");
107+
@regular: local("@{family-str}@{2ndlightest}");
108+
@medium: local("@{family-str}@{2ndboldest}");
109+
@bold: local("@{family-str}@{boldest}");
110+
}
111+
.gen-weights(@family) when not (isstring(@family)) {
112+
.gen-weights-all(@family);
113+
}
114+
.gen-weights(@family, @last) {
115+
@this: .gen-weights(@family);
116+
117+
@light: @last[@light], @this[@light];
118+
@regular: @last[@regular], @this[@regular];
119+
@medium: @last[@medium], @this[@medium];
120+
@bold: @last[@bold], @this[@bold];
121+
}
122+
.gen-weights-all(@family) when not (isstring(@family)) {
123+
.gen-weights-all(@family, length(@family));
124+
}
125+
.gen-weights-all(@family, 1) when not (isstring(@family)) {
126+
.gen-weights(extract(@family, 1));
127+
}
128+
.gen-weights-all(@family, @ctr) when not (isstring(@family)) and (@ctr > 1) and (@ctr <= length(@family)) {
129+
.gen-weights(extract(@family, @ctr), .gen-weights-all(@family, @ctr - 1));
130+
}

web_src/less/index.less

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
@import "_svg";
1515
@import "_tribute";
16+
@import "_font_i18n";
1617
@import "_base";
1718
@import "_markdown";
1819
@import "_home";

0 commit comments

Comments
 (0)